Comunicación I2C entre Arduino y STM32
En esta entrada aprenderemos como enviar variables desde un STM32F103 (Blue Pill) a un Arduino Nano vía I2C. Por utilizar un ejemplo real y que pueda tener utilidad, enviaremos por I2C las lecturas de un sensor MPU6050: el STM32 hará lecturas del sensor MPU6050 y las enviara al Arduino Nano vía I2C para que este las enseñe por comunicación serie.
Comprar STM32 Comprar Arduino Nano
Conexión entre MPU6050, STM32 y Arduino
La placa STM32 tiene dos puertos I2C, por lo que utilizaremos uno para leer el sensor MPU6050, y el otro para comunicarnos con el Arduino Nano:
- PB11 (STM32), I2C2 ⇒ SDA (MPU6050)
- PB10 (STM32), I2C2 ⇒ SCL (MPU6050)
- PB6 (STM32), I2C1 ⇒ A5 (Arduino Nano)
- PB7 (STM32), I2C1 ⇒ A4 (Arduino Nano)
Envío de variables vía I2C
El proceso para enviar variables por I2C consta de dos partes:
- Descomponer cada variable en bytes.
- Enviar por I2C cada byte por separado, uno a uno.
En este ejemplo vamos a transmitir 6 variables de 2 bytes (las lecturas del MPU6050), 12 bytes en total. Para ello, guardaremos estas 6 variables en un array y utilizar un bucle for para recorrerlo, descomponiendo en cada iteración cada variable en sus correspondientes 2 bytes, y enviando cada byte uno a uno por I2C. Este es el código necesario para enviar cada paquete de 6 variables, y que cargaremos en el STM32:
Wire.beginTransmission(4); // Comenzar transmision con dirección 4
for (int i = 0; i < 6; i++) { // Recorrer una a una las variables a enviar
byte* variables_byte = (byte*)&variables[i]; // Transformar cada variable a bytes
Wire.write(variables_byte, 2); // Enviar cada variable (2 bytes)
}
Wire.endTransmission(); // Terminar transmision con dirección 4
Para recibir estas variables (Arduino Nano) utilizaremos esta línea de código. Para recibir las variables utilizaremos también un bucle for, donde en cada iteración volveremos a recomponer cada variable a partir de los paquetes de 2 bytes que recibamos. En este ejemplo, necesitamos un bucle for con 6 iteraciones, una por cada variables que esperamos recibir:
while (Wire.available() < 6 * 2)delay(1);
for (int i = 0; i < 6; i++) variables_NANO[i] = Wire.read() | Wire.read() << 8;
Como veis, el receptor tiene que saber de antemano cuantas variables y de qué tamaño espera recibir.
Software completo
Emisor (STM32)
#include <Wire.h> // I2C1 para comunicación con Arduino Nano
TwoWire HWire (2, I2C_FAST_MODE); // I2C2 para comunicación con MPU6050
#define gyro_address 0x68
int16_t gyro_x, gyro_y, gyro_z, temperature;
int16_t acc_x, acc_y, acc_z;
int16_t variables_STM32[6];
int32_t loop_timer;
void setup() {
Wire.setClock(400000); // Velocidad 400kHz
Wire.begin(); // Inicializar I2C1
HWire.setClock(400000); // Velocidad 400kHz
HWire.begin(); // Inicializar I2C2
Serial.begin(115200); // Inicializar comunicacion serie
init_gyro(); // Inicializar MPU
}
void loop() {
// Ejecutamos el loop cada 4ms
while (micros() - loop_timer < 4000);
loop_timer = micros();
// Leer MPU
MPU_6050();
// Generar array con las lecturas del MPU6050
variables_STM32[0] = acc_x;
variables_STM32[1] = acc_y;
variables_STM32[2] = acc_z;
variables_STM32[3] = gyro_x;
variables_STM32[4] = gyro_y;
variables_STM32[5] = gyro_z;
Serial.print(variables_STM32[0]);
Serial.print("\t");
Serial.print(variables_STM32[1]);
Serial.print("\t");
Serial.print(variables_STM32[2]);
Serial.print("\t");
Serial.print(variables_STM32[3]);
Serial.print("\t");
Serial.print(variables_STM32[4]);
Serial.print("\t");
Serial.println(variables_STM32[5]);
Wire.beginTransmission(4); // Comenzar transmision con dirección 4
for (int i = 0; i < 6; i++) { // Recorrer una a una las variables a enviar
byte* variables_byte = (byte*)&variables_STM32[i]; // Transformar cada variable a bytes
Wire.write(variables_byte, 2); // Enviar cada variable (2 bytes)
}
Wire.endTransmission(); // Terminar transmision con dirección 4
}
//================= Leer MPU6050
void MPU_6050() {
HWire.beginTransmission(0x68);
HWire.write(0x3B);
HWire.endTransmission();
HWire.requestFrom(0x68, 14);
while (HWire.available() < 14);
acc_x = HWire.read() << 8 | HWire.read();
acc_y = HWire.read() << 8 | HWire.read();
acc_z = HWire.read() << 8 | HWire.read();
temperature = HWire.read() << 8 | HWire.read();
gyro_x = HWire.read() << 8 | HWire.read();
gyro_y = HWire.read() << 8 | HWire.read();
gyro_z = HWire.read() << 8 | HWire.read();
}
//================= Subrutina inicilialización (solo se ejecuta una vez al iniciar el programa)
void init_gyro() {
HWire.beginTransmission(0x68);
HWire.write(0x6B);
HWire.write(0x00);
HWire.endTransmission();
//Configure the accelerometer (+/-8g)
HWire.beginTransmission(0x68);
HWire.write(0x1C);
HWire.write(0x10);
HWire.endTransmission();
//Configure the gyro (500dps full scale)
HWire.beginTransmission(0x68);
HWire.write(0x1B);
HWire.write(0x08);
HWire.endTransmission();
}
Receptor (Arduino Nano)
#include <Wire.h>
int16_t variables_NANO[6];
void setup() {
Wire.begin(4); // Asignamos la dirección 4 al Arduino Nano
Wire.setClock(400000); // Velocidad 400kHz
Wire.onReceive(receiveEvent); // Necesario para recibir datos por I2C
Serial.begin(115200);
}
void loop() {
// Hasta no recibir 6*2 = 12 bytes, no hacer nada
while (Wire.available() < 6 * 2)delay(1);
// Recomponer cada variable a partir de cada paquete de 2 bytes
for (int i = 0; i < 6; i++) variables_NANO[i] = Wire.read() | Wire.read() << 8;
// Visualizar variables recibidas
Serial.print(variables_NANO[0]);
Serial.print("\t");
Serial.print(variables_NANO[1]);
Serial.print("\t");
Serial.print(variables_NANO[2]);
Serial.print("\t");
Serial.print(variables_NANO[3]);
Serial.print("\t");
Serial.print(variables_NANO[4]);
Serial.print("\t");
Serial.println(variables_NANO[5]);
}
void receiveEvent(int howMany) {
}
Thanks for all the good work!
When loading the emitter program into the Arduino IDE and verifying the following error comes up:
Arduino:1.8.15 (Mac OS X), Board:»Generic STM32F1 series, Generic F103C8Tx, STM32CubeProgrammer (SWD), Enabled (generic ‘Serial’), None, Low/Full Speed, Smallest (-Os default), None, Newlib Nano (default)»
exit status 1
‘I2C_FAST_MODE’ was not declared in this scope; did you mean ‘IS_UART_MODE’?
Do you know what causes this problem?
I would appreciate your answer and thank you for it.