Llevo años trasteando con microcontroladores. Y sí. El 8051 sigue vivo. No es el más potente, no tiene periféricos modernos, pero para proyectos de bajo coste y alto control… es una bestia parda. Pero ojo: conectarlo a un acelerómetro moderno como el ADXL345 no es trivial. El bus I2C del 8051 no es nativo. Hay que hacerlo por software. Bit-banging puro y duro.
En DunaTech, pasamos meses cribando basura en AliExpress. Y el ADXL345 es de esos sensores que encuentras por 2€ y funcionan de puta madre… si sabes cómo programarlos. Si no, te vuelves loco con lecturas a 0x00 o valores que no cambian. Aquí te voy a dar el código, las librerías .h y los trucos que he aprendido rompiendo tres módulos en el proceso.
Hardware: Conexión ADXL345 + 8051
Primero, lo básico. El ADXL345 se comunica por I2C o SPI. Nosotros vamos a usar I2C porque solo necesitas dos pines (SDA y SCL) y es más fácil de implementar en un 8051 genérico (como el AT89C51 o AT89S52).
Diagrama de conexión
- VCC → 3.3V (¡ojo! el 8051 suele ser 5V, pero el ADXL345 es de 3.3V. Necesitas un regulador o nivel lógico).
- GND → GND común.
- SDA → Pin P1.0 (ejemplo, puedes usar cualquier pin).
- SCL → Pin P1.1.
- CS → A VCC (para seleccionar modo I2C).
- SDO/ALT ADDRESS → A GND (dirección I2C 0x53) o VCC (0x1D).
Y sí. Necesitas resistencias pull-up de 4.7kΩ en SDA y SCL. Si no las pones, el bus no funciona. Error de novato que yo mismo he cometido.
La Librería .h: Nuestro Código Base
Aquí está el core del asunto. Una librería adxl345.h que encapsula toda la comunicación I2C y los registros del ADXL345. La he probado con Keil C51 y SDCC. Funciona en ambos.
// adxl345.h
#ifndef __ADXL345_H__
#define __ADXL345_H__
// Dirección I2C del ADXL345 (SDO a GND = 0x53)
#define ADXL345_ADDR 0x53
// Registros del ADXL345
#define DEVID 0x00
#define POWER_CTL 0x2D
#define DATA_FORMAT 0x31
#define DATAX0 0x32
#define DATAX1 0x33
#define DATAY0 0x34
#define DATAY1 0x35
#define DATAZ0 0x36
#define DATAZ1 0x37
// Funciones públicas
void adxl345_init(void);
void adxl345_read_xyz(int *x, int *y, int *z);
// Funciones I2C (implementación en .c)
void i2c_start(void);
void i2c_stop(void);
void i2c_write(unsigned char dat);
unsigned char i2c_read(unsigned char ack);
#endif
Fíjate en los registros. El POWER_CTL (0x2D) es clave: hay que escribir 0x08 para salir del modo sleep. Sin eso, el sensor no mide nada. El DATA_FORMAT (0x31) configura el rango: 0x00 para ±2g, 0x03 para ±16g. Yo uso 0x0B que es ±16g con resolución completa (13 bits).
Programas en C: Lectura por I2C
Ahora, el código que hace la magia. Implementación del bit-banging I2C y la lectura del acelerómetro.
Implementación I2C por Software
// i2c.c
#include <reg51.h>
#include "adxl345.h"
sbit SDA = P1^0;
sbit SCL = P1^1;
void i2c_delay(void) {
unsigned char i;
for(i=0; i<10; i++); // Ajusta según tu frecuencia de reloj
}
void i2c_start(void) {
SDA = 1;
SCL = 1;
i2c_delay();
SDA = 0;
i2c_delay();
SCL = 0;
}
void i2c_stop(void) {
SDA = 0;
SCL = 1;
i2c_delay();
SDA = 1;
i2c_delay();
}
void i2c_write(unsigned char dat) {
unsigned char i;
for(i=0; i<8; i++) {
SDA = (dat & 0x80) ? 1 : 0;
dat <<= 1;
SCL = 1;
i2c_delay();
SCL = 0;
i2c_delay();
}
// Esperar ACK
SDA = 1;
SCL = 1;
i2c_delay();
SCL = 0;
}
unsigned char i2c_read(unsigned char ack) {
unsigned char i, dat = 0;
SDA = 1;
for(i=0; i<8; i++) {
dat <<= 1;
SCL = 1;
i2c_delay();
if(SDA) dat |= 1;
SCL = 0;
i2c_delay();
}
// Enviar ACK o NACK
SDA = ack ? 0 : 1;
SCL = 1;
i2c_delay();
SCL = 0;
return dat;
}
Inicialización y Lectura del ADXL345
// adxl345.c
#include <reg51.h>
#include "adxl345.h"
void adxl345_init(void) {
i2c_start();
i2c_write(ADXL345_ADDR << 1); // Dirección + escritura
i2c_write(POWER_CTL);
i2c_write(0x08); // Modo medición
i2c_stop();
i2c_start();
i2c_write(ADXL345_ADDR << 1);
i2c_write(DATA_FORMAT);
i2c_write(0x0B); // ±16g, resolución completa
i2c_stop();
}
void adxl345_read_xyz(int *x, int *y, int *z) {
unsigned char xl, xh, yl, yh, zl, zh;
// Escribir dirección del registro inicial
i2c_start();
i2c_write(ADXL345_ADDR << 1);
i2c_write(DATAX0);
i2c_stop();
// Leer 6 bytes (X, Y, Z en little-endian)
i2c_start();
i2c_write((ADXL345_ADDR << 1) | 1); // Dirección + lectura
xl = i2c_read(1);
xh = i2c_read(1);
yl = i2c_read(1);
yh = i2c_read(1);
zl = i2c_read(1);
zh = i2c_read(0); // Último byte con NACK
i2c_stop();
*x = (xh << 8) | xl;
*y = (yh << 8) | yl;
*z = (zh << 8) | zl;
}
Y así de simple. Llamas a adxl345_init() una vez, y luego adxl345_read_xyz() cada vez que necesites datos. Los valores son enteros con signo de 16 bits. Para convertirlos a gravedades (g), multiplicas por la escala: con configuración ±16g, cada LSB son 3.9 mg (0.0039 g).
Troubleshooting & Errores Comunes
Aquí es donde la mayoría de la gente tira la toalla. He visto decenas de posts en foros con el mismo problema. Te ahorro meses de debugging.
Error 1: Lecturas a 0x00 o 0xFFFF
Si lees todo ceros o todo Fs, el I2C no está funcionando. Revisa:
- Pull-ups: ¿Tienes resistencias de 4.7kΩ en SDA y SCL? Sin ellas, el bus no puede generar el nivel alto.
- Dirección I2C: ¿Conectaste SDO a GND? Entonces la dirección es 0x53. ¿A VCC? Entonces 0x1D. Si pones la incorrecta, el sensor no responde.
- Nivel lógico: El ADXL345 es de 3.3V. Si tu 8051 es de 5V, necesitas un conversor de nivel. Un divisor resistivo en SDA y SCL vale. Sin eso, quemas el sensor o no comunica.
Error 2: El sensor solo lee un eje
Probablemente el registro DATA_FORMAT no está bien configurado. Asegúrate de escribir 0x0B (o 0x00 para ±2g). Si lo dejas sin inicializar, el sensor usa valores por defecto que pueden ser extraños.
Error 3: Los valores no cambian al mover el sensor
Estás en modo sleep. Revisa que escribiste 0x08 en POWER_CTL. Sin eso, el sensor está dormido y siempre lee lo mismo. He visto código que se salta este paso y la gente piensa que el sensor está roto.
Error 4: Ruido excesivo en las lecturas
El ADXL345 tiene un filtro digital. Puedes configurar la tasa de salida (register BW_RATE, 0x2C). Por defecto es 100 Hz. Si necesitas menos ruido, baja a 12.5 Hz (valor 0x09). El código sería:
i2c_start();
i2c_write(ADXL345_ADDR << 1);
i2c_write(0x2C); // BW_RATE
i2c_write(0x09); // 12.5 Hz
i2c_stop();
FAQ
¿Puedo usar SPI en lugar de I2C?
Sí, el ADXL345 soporta SPI. Pero necesitas más pines (CS, MOSI, MISO, SCLK) y el 8051 no tiene SPI nativo. Tendrías que hacer bit-banging también. I2C es más sencillo para este micro.
¿Funciona con un cristal de 12 MHz?
Sí, pero ajusta los delays en i2c_delay(). Con 12 MHz, el bucle de 10 iteraciones da una frecuencia I2C de unos 100 kHz, que es perfecta. Si usas 24 MHz, duplica el bucle.
¿Dónde compro el ADXL345 en AliExpress sin que me timen?
Nosotros en DunaTech siempre recomendamos comprar los módulos con el pinout claramente marcado. Los que vienen en una placa azul con el chip soldado suelen ser fiables. Evita los que venden sueltos sin PCB, porque a menudo son falsificaciones o están dañados. Y ojo: si buscas otros cacharros para tu taller, échale un ojo a nuestra guía de las 5 tablets más baratas de AliExpress en 2026 que también usan buses I2C para sensores.
Conclusión
El ADXL345 es un sensor cojonudo para proyectos con 8051. Barato, preciso y fácil de integrar si tienes las librerías adecuadas. El problema es que la documentación oficial asume que usas un Arduino o un micro moderno. Nosotros, los que trasteamos con 8051, tenemos que hacer el trabajo sucio. Pero merece la pena.
Con el código que te he dado, tienes una base sólida. Inicialización, lectura por I2C y conversión a gravedades. Los errores más comunes son siempre los mismos: pull-ups, dirección I2C y el maldito modo sleep. Si evitas esos tres, el sensor te funcionará a la primera.
En DunaTech seguimos cribando el desierto de AliExpress para encontrar componentes que realmente valgan la pena. El ADXL345 es uno de esos oasis. Ahora ve y programa tu 8051.
Ver precio actual y cupones en AliExpress


