STM32 microcontrolador lee datos del sensor de temperatura y humedad AHT10

STM32 lee datos del sensor de temperatura y humedad AHT10 mediante I2C por hardware y los muestra en una pantalla OLED de 0,96 pulgadas.

Utilizo el microcontrolador STM32F103C8T6 y el programa está escrito con la biblioteca estándar de ST.

STM32 lee el sensor de temperatura y humedad SHTC3 mediante I2C por hardware: https://blog.zeruns.com/archives/692.html

Grupo de tecnología de electrónica/microcontroladores: 2169025065

Resultado visual del proyecto


Introducción al protocolo I2C

El protocolo de comunicación I2C (Inter-Integrated Circuit) fue desarrollado por Philips. Debido a su bajo número de pines, implementación hardware simple, alta escalabilidad y la ausencia de dispositivos de transmisión/recepción externos como los necesarios en USART o CAN (esos chips de conversión de niveles), hoy se utiliza ampliamente para la comunicación entre múltiples circuitos integrados (IC) dentro de un sistema.

I2C solo dispone de un bus de datos SDA (Serial Data Line), un bus de datos en serie que transmite bit a bit; por tanto es una comunicación serie y semidúplex.

Comunicación semidúplex: permite comunicación bidireccional, pero no simultáneamente en ambas direcciones; debe alternarse por turnos. Se puede entender como una comunicación simplex con dirección variable: en un momento dado solo puede transmitirse en un sentido y basta con una línea de datos.
Para el protocolo I2C distinguimos capa física y capa de protocolo: la física define las características mecánicas y electrónicas (hardware) que garantizan la transmisión de datos brutos por el medio; la de protocolo regula la lógica de comunicación, estandarizando la forma de empaquetar y desempaquetar los datos entre emisor y receptor (software).

Capa física de I2C

Conexión típica entre dispositivos I2C

  1. 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 dispositivos, admitiendo varios maestros y varios esclavos.

  2. Un bus I2C solo necesita dos líneas: una línea de datos bidireccional SDA (Serial Data Line) y una línea de reloj SCL (Serial Clock Line). La línea de datos transporta la información y la de reloj sincroniza la transmisión.

  3. El bus se conecta a la alimentación mediante resistencias de pull-up. Cuando un dispositivo I2C está inactivo presenta alta impedancia; si todos los dispositivos están inactivos, las resistencias de pull-up llevan el bus al nivel alto.

Durante la comunicación I2C los pines GPIO del microcontrolador deben configurarse como salida drenador abierto; de lo contrario puede producirse un cortocircuito.

Para más información sobre I2C en STM32 y su uso, consulte este artículo: https://url.zeruns.com/JC0Ah
Aquí no lo explicaré en detalle.

Sensor de temperatura y humedad AHT10

Introducción

El AHT10 es un sensor de temperatura y humedad fabricado en China, barato, preciso y de pequeño tamaño.

Incluye un nuevo ASIC diseñado específicamente, un elemento capacitivo MEMS mejorado para medir humedad y un sensor de temperatura integrado estándar. Su rendimiento supera con creces la fiabilidad de la generación anterior, manteniéndose estable incluso en entornos hostiles.

Descarga del datasheet del AHT10: https://url.zeruns.com/EDEwF


Del datasheet se extrae:

  • Rango de temperatura: –40 °C ‑ 85 °C
  • Error de temperatura: ±0,3 °C
  • Rango de humedad: 0 % ‑ 100 %
  • Error de humedad: ±2 %
  • Voltaje de trabajo: 1,8 V ‑ 3,6 V
  • Método de comunicación: I2C
  • Frecuencia de reloj: 100 kHz y 400 kHz

Dirección del dispositivo y comandos de lectura/escritura

Durante el uso real, la dirección del AHT10 debe combinarse con el bit de dirección de lectura/escritura para formar un byte completo que se envía tras el bit de inicio; el bit menos significativo indica la operación (lectura o escritura) y los 7 bits superiores son la dirección del dispositivo.

Para escribir datos o comandos: tras la señal de inicio se envía “0111 0000”, es decir, 0x70. Los 7 bits altos (“0111 000”) seleccionan el dispositivo y el bit bajo (“0”) indica escritura.

Para leer datos: tras el inicio se envía “0111 0001”, 0x71, donde el bit bajo (“1”) indica lectura.
En resumen: 0x70 para escritura, 0x71 para lectura. Con la I2C por hardware de STM32 basta con indicar 0x70; la biblioteca estándar gestiona el bit de lectura/escritura.

Lectura de datos de temperatura y humedad


Según el datasheet, un ciclo de medida consta de:

  1. Enviar comando de medida
  2. Esperar a que finalice la medida
  3. Leer los datos obtenidos

Resumen:

  1. Enviar comando de medida: primero dirección de escritura (0x70), luego comando de disparo (0xAC) y parámetros del comando (0x33 y 0x00).
  2. Esperar: el datasheet indica 75 ms; basta con esperar algo más.
  3. Recibir datos: enviar dirección de lectura (0x71) y leer 6 bytes consecutivos. El primero es el byte de estado; comprobar que el bit 3 (calibración) valga 1; si no, enviar comando de inicialización. Comprobar el bit 7 (ocupado/libre); si es 0 la medida ha terminado y se procede.
  4. Procesar los datos recibidos.

Cálculo de los datos

Según el datasheet del AHT10:

Ejemplo: si el valor de humedad leído es 0x0C6501, en decimal es 812289.
Entonces: humedad = 812289 × 100 / 1048576 = 77,46 %
Si el valor de temperatura es 0x056A00, decimal 354816.
Entonces: temperatura = (354816 × 200 / 1048576) − 50 = 17,67 °C

Componentes necesarios

Placa mínima STM32: https://s.click.taobao.com/bqMwZRu
Módulo AHT10: https://s.click.taobao.com/gIF09Ru
Módulo OLED: https://s.click.taobao.com/aNlvZRu
Cables Dupont: https://s.click.taobao.com/xAkAJRu
Protoboard: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu

Código

Aquí solo muestro los tres archivos principales: main.c, AHT10.c y OLED.c. El resto está en el paquete comprimido del enlace.

Proyecto completo: https://url.zeruns.com/AHT10

SCL de los módulos AHT10 y OLED a PB6, SDA a PB7.

Usar VSCode en lugar de Keil para desarrollar con STM32 y 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();	// Inicializar watchdog
	OLED_Init();			// Inicializar pantalla OLED
	AHT10_Init();			// Inicializar 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); // Contador para verificar que el programa sigue activo
		if(a==999999999)a=0;

		float Temp,Hum;			// Variables para almacenar temperatura y humedad
		
	/*
	https://blog.zeruns.com
	*/

		if(ReadAHT10(&Hum,&Temp))	// Leer temperatura y humedad
		{
			if(Temp>=0)
			{
				char String[10];
				sprintf(String, "+%.2fC", Temp); // Formatear cadena
				OLED_ShowString(1, 3, String);	 // Mostrar temperatura

				sprintf(String, " %.2f%%", Hum); // Formatear cadena
				OLED_ShowString(2, 3, String);	 // Mostrar humedad
			}else
			{
				char String[10];
				sprintf(String, "-%.2fC", Temp); // Formatear cadena
				OLED_ShowString(1, 3, String);	 // Mostrar temperatura
				
				sprintf(String, " %.2f%%", Hum); // Formatear cadena
				OLED_ShowString(2, 3, String);	 // Mostrar humedad
			}
		}
		else
		{
			err_count++;
			OLED_ShowNum(4,1, err_count, 5);	// Mostrar contador de errores
		}

		Delay_ms(100);	// Retardo 100 ms

		IWDG_FeedDog();	// Alimentar watchdog (si transcurre más de 1 s sin hacerlo se reinicia el sistema)
	}
}
```**AHT10.c**

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

/*Dirección AHT10*/
#define AHT10_ADDRESS 0x38<<1 //La dirección del esclavo es de 7 bits, el último bit indica la dirección de transmisión, por lo tanto se desplaza un bit a la izquierda

/*Configurar qué I2C se utiliza*/
#define I2Cx I2C1

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


/*Enviar señal de inicio*/
void AHT10_I2C_START(){
    while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//Esperar a que el bus esté libre
	I2C_GenerateSTART(I2Cx, ENABLE);//Enviar señal de inicio
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//Detectar evento EV5
}

/*Enviar señal de parada*/
void AHT10_I2C_STOP(){
    I2C_GenerateSTOP(I2Cx, ENABLE);//Enviar señal de parada
}

/**
  * @brief  Enviar 3 bytes de datos
  * @param  cmd byte de comando
  * @param  DATA0 primer parámetro
  * @param  DATA1 segundo parámetro
  * @retval Ninguno
  */
void AHT10_WriteByte(uint8_t cmd, uint8_t DATA0, uint8_t DATA1)
{
	AHT10_I2C_START();  //Enviar señal de inicio
	
	I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    //Enviar dirección de escritura del dispositivo
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //Detectar evento EV6

    I2C_SendData(I2Cx, cmd);//Enviar comando
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Detectar evento EV8

    I2C_SendData(I2Cx, DATA0);//Enviar los 8 bits altos del parámetro del comando
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Detectar evento EV8
   
	I2C_SendData(I2Cx, DATA1);//Enviar los 8 bits bajos del parámetro del comando
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Detectar evento EV8
	
	I2C_GenerateSTOP(I2Cx, ENABLE);//Enviar señal de parada	
}


/**
  * @brief  Enviar comando para leer el estado del AHT10
  * @retval byte de estado leído
  */
/*uint8_t AHT10_ReadStatus(void){
    AHT10_I2C_START();//Enviar señal de inicio  
	I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver);//Enviar dirección de lectura del dispositivo
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Detectar evento EV6
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Detectar evento EV7
	I2C_AcknowledgeConfig(I2Cx, DISABLE); //Desactivar señal de respuesta
	uint8_t status = I2C_ReceiveData(I2Cx);//Leer datos y devolver
	AHT10_I2C_STOP();   //Enviar señal de parada
	I2C_AcknowledgeConfig(I2Cx,ENABLE);//Volver a activar la señal de respuesta
	return status;
}*/

/**
  * @brief  Leer datos
  * @retval byte de datos leído
  */
uint8_t AHT10_ReadData(void)
{
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Detectar evento EV7
	return I2C_ReceiveData(I2Cx);//Leer datos y devolver
}

/*Reinicio por software del AHT10*/
void AHT10_SoftReset(void)                    
{
    AHT10_I2C_START();  //Enviar señal de inicio
	I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    //Enviar dirección de escritura del dispositivo
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //Detectar evento EV6
    I2C_SendData(I2Cx, 0xBA);//Enviar comando de reinicio por software
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Detectar evento EV8
    I2C_GenerateSTOP(I2Cx, ENABLE);//Enviar señal de parada
	Delay_ms(20);
}


/*Inicialización de pines*/
void AHT10_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//Habilitar reloj de I2C1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Habilitar reloj de GPIOB
 
	/*I2C1 por hardware del chip STM32F103: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;               //Definir estructura para configurar 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;		//Configurar modo de salida como drenaje abierto, requiere resistencia de pull-up
	GPIO_Init(GPIOB, &GPIO_InitStructure);              //Inicializar GPIO
	
	I2C_DeInit(I2Cx);	//Restablecer registros del periférico I2C a valores predeterminados
	I2C_InitTypeDef  I2C_InitStructure;                 //Definir estructura para configurar I2C
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//Modo de trabajo
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//Ciclo de trabajo del reloj, Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x88;	//Dirección I2C del host, si no se usa se puede escribir cualquier valor, no afecta
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//Habilitar bit de respuesta
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Establecer longitud de dirección a 7 bits
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//Velocidad de transmisión I2C, 400K, verificar en el manual del chip la velocidad soportada.	
	I2C_Init(I2Cx, &I2C_InitStructure);         //Inicializar I2C

	I2C_Cmd(I2Cx, ENABLE);  //Habilitar I2C
	Delay_ms(20);//Retraso al encender
	AHT10_WriteByte(0XE1,0X08,0x00);//Enviar instrucción de inicialización
	Delay_ms(20);

}

/**
  * @brief  Leer datos del AHT10
  * @param  *Hum humedad
  * @param  *Temp temperatura
  * @retval 1 - lectura exitosa; 0 - lectura fallida
  */
uint8_t ReadAHT10(float *Hum,float *Temp)
{
    uint8_t Data[5];//Declarar variable para almacenar datos leídos

	AHT10_WriteByte(0XAC,0X33,0x00);//Enviar instrucción para activar medición

	Delay_ms(70);	//Retraso de 70 ms esperando a que se complete la medición

    AHT10_I2C_START();//Enviar señal de inicio  
	I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver);//Enviar dirección de lectura del dispositivo
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Detectar evento EV6
	
	uint8_t i;
	for(i=0;i<6;i++)//Ciclo 6 veces para leer 6 bytes de datos
	{
		if (i == 5)	//Al leer el último byte, desactivar la señal de respuesta
		{
			I2C_AcknowledgeConfig(I2Cx, DISABLE); //Desactivar señal de respuesta
		}
		Data[i] = AHT10_ReadData();	//Leer datos
		if (i == 5)
			I2C_GenerateSTOP(I2Cx, ENABLE); //Enviar señal de parada
	}
	I2C_AcknowledgeConfig(I2Cx,ENABLE);//Volver a activar la señal de respuesta

	if( (Data[0]&0x08) == 0 )//0x08(00001000) verificar si el bit 3 del byte de estado (bit de habilitación de calibración) es 0
	{
		AHT10_WriteByte(0XE1,0X08,0x00);	//Enviar instrucción de inicialización
		Delay_ms(20);
		return 0;
	}
	else if( (Data[0]&0x80) == 0 )//0x80(10000000) verificar si el bit 7 del byte de estado (indicador de ocupado) es 0
	{
		
		uint32_t SRH = (Data[1]<<12) | (Data[2]<<4) | (Data[3]>>4);	//Procesar datos de humedad
		uint32_t ST = ((Data[3]&0x0f)<<16) | (Data[4]<<8) | Data[5];//Procesar datos de temperatura

		*Hum = (SRH * 100.0) / 1024.0 / 1024;	   //Convertir datos de humedad según la fórmula del manual
		*Temp = (ST * 200.0) / 1024.0 / 1024 - 50; //Convertir datos de temperatura según la fórmula del manual

		return 1;
	}

	I2C_GenerateSTOP(I2Cx, ENABLE);//Enviar señal de parada
	return 0;	

}

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

```c
#include "stm32f10x.h"
#include "OLED_Font.h"

/*Dirección de la pantalla OLED*/
#define OLED_ADDRESS 0x78

/*Configurar cuál I2C se usa*/
#define I2Cx I2C1

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

/*Inicialización de pines*/
void OLED_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//Habilitar reloj de I2C1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //Habilitar reloj de GPIOB

	/*I2C por hardware del chip 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;		//Modo de salida drenaje abierto, requiere resistencia de pull-up
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_DeInit(I2Cx);	//Restablecer registros I2C a valores por defecto
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//Modo de trabajo
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//Ciclo de trabajo del reloj, Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x88;	//Dirección I2C del maestro, si no se usa se puede poner cualquier valor
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//Habilitar bit de reconocimiento
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Dirección de 7 bits
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//Velocidad de transmisión I2C, 400K, verificar en el manual del chip utilizado.
	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));
	
	//Enviar señal de inicio
	I2C_GenerateSTART(I2Cx, ENABLE);
	//Detectar evento EV5
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//Enviar dirección de escritura del dispositivo
	I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
	//Detectar evento EV6
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	
	//Enviar dirección interna del dispositivo a operar
	I2C_SendData(I2Cx, addr);
	//Detectar evento EV8_2
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(I2Cx, data);//Enviar dato
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	//Enviar señal de parada
	I2C_GenerateSTOP(I2Cx, ENABLE);
}
 
/**
  * @brief  Escribir comando en OLED
  * @param  Command comando a escribir
  * @retval Ninguno
  */
void OLED_WriteCommand(unsigned char Command)//Escribir comando
{
	I2C_WriteByte(0x00, Command);
}
 
/**
  * @brief  Escribir dato en OLED
  * @param  Data dato a escribir
  * @retval Ninguno
*/
void OLED_WriteData(unsigned char Data)//Escribir dato
{
	I2C_WriteByte(0x40, Data);
}

/**
  * @brief  Establecer posición del cursor en OLED
  * @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 bits altos de X
	OLED_WriteCommand(0x00 | (X & 0x0F));			//Establecer bits bajos de X
}

/**
  * @brief  Limpiar pantalla OLED
  * @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  Limpiar parte de la pantalla OLED
  * @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 parte superior
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);			//Mostrar parte superior
		}
		OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//Establecer cursor en parte inferior
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);		//Mostrar parte inferior
		}
	}
}

/**
  * @brief  Mostrar un carácter en OLED
  * @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 parte superior
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//Mostrar parte superior
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//Establecer cursor en parte inferior
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//Mostrar parte inferior
	}
}

/**
  * @brief  Mostrar cadena en OLED
  * @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 de potencia para OLED
  * @retval Retorna 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  Mostrar número en OLED (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  Mostrar número en OLED (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  Mostrar número en OLED (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  Mostrar número en OLED (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  Inicializar OLED
  * @param  Ninguno
  * @retval Ninguno
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//Retardo tras encendido
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//Inicializar puertos
	
	OLED_WriteCommand(0xAE);	//Apagar pantalla
	
	OLED_WriteCommand(0xD5);	//Establecer división de reloj/frecuencia del oscilador
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//Establecer ratio de multiplexado
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//Establecer desplazamiento de pantalla
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//Establecer línea de inicio de pantalla
	
	OLED_WriteCommand(0xA1);	//Establecer dirección izquierda-derecha, 0xA1 normal 0xA0 invertido
	
	OLED_WriteCommand(0xC8);	//Establecer dirección arriba-abajo, 0xC8 normal 0xC0 invertido

	OLED_WriteCommand(0xDA);	//Configuración de pines COM
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//Control de contraste
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//Período de precarga
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//Nivel de deselección VCOMH
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//Encender/apagar pantalla completa

	OLED_WriteCommand(0xA6);	//Pantalla normal/invertida

	OLED_WriteCommand(0x8D);	//Bomba de carga
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//Encender pantalla
		
	OLED_Clear();				//Limpiar pantalla
}

Recomendaciones de lectura

1 me gusta