UNIVERSIDAD AUTONOMA DE QUERETARO
CAMPUS SAN JUAN DEL RIO
Protocolos de comunicación
Alumno:
Oscar Ruiz Rivera – 283294
Materia:
Laboratorio de Microcontroladores
Profesor:
Leonardo Esteban Moreno Suárez
Fecha:
10/10/24
I. Objetivo
Mediante la utilización de los protocolos UART con un módulo bluetooth, SPI con una pantalla TFT e I2C con
un sensor de giroscopio, implementar los valores mostrados del sensor en la pantalla TFT como a su vez
mostrar los valores obtenidos en tiempo real en la interfaz de Matlab mandando los datos con el módulo
bluetooth.
II. Introducción
PROTOCOLOS DE COMUNICACIÓN
Los protocolos de comunicación son fundamentales para garantizar el intercambio eficiente y estructurado de
datos entre dispositivos o sistemas. Se definen como conjuntos de reglas y normas que especifican cómo se
transmiten, reciben e interpretan los datos, abarcando aspectos como el formato de los mensajes, la
sincronización, la corrección de errores y la gestión de fallos.
Estos protocolos son clave en diversas áreas, desde redes de computadoras y telecomunicaciones hasta
sistemas embebidos y el Internet de las Cosas (IoT). Su principal objetivo es asegurar la compatibilidad y el
correcto funcionamiento entre dispositivos de diferentes fabricantes. Ejemplos destacados incluyen TCP/IP para
internet, UART e I2C en comunicación serial y Bluetooth o Wi-Fi para enlaces inalámbricos.
COMUNICACIÓN UART
El protocolo UART (Universal Asynchronous Receiver-Transmitter) es un método de comunicación serial
ampliamente utilizado en sistemas embebidos y dispositivos electrónicos. Este protocolo permite la transmisión
de datos de manera asíncrona, es decir, sin necesidad de una señal de reloj externa. La comunicación UART
utiliza dos líneas: una para la transmisión de datos (TX) y otra para la recepción (RX). Los datos se envían en
paquetes que incluyen un bit de inicio, los bits de datos (generalmente 8 bits), un bit de paridad (opcional) para
detección de errores, y uno o más bits de parada que marcan el final del paquete. Ambos dispositivos deben
estar configurados con la misma velocidad de baudios para asegurar la correcta transmisión de los datos.
Aunque UART es simple y eficiente, su alcance está limitado a distancias cortas y no es ideal para múltiples
dispositivos, ya que es un protocolo punto a punto. A pesar de estas limitaciones, su uso sigue siendo común
en aplicaciones como la conexión de microcontroladores con periféricos, módulos de comunicación (Bluetooth,
GPS, Wi-Fi) y dispositivos de monitoreo.
Fig.1. Configuración protocolo UART.
COMUNICACIÓN SPI
El protocolo SPI (Serial Peripheral Interface) es una interfaz de comunicación serial síncrona utilizada para la
transferencia de datos entre un microcontrolador y dispositivos periféricos, como sensores, memorias y módulos
de comunicación. A diferencia de UART, SPI requiere una señal de reloj (SCK) generada por el maestro para
sincronizar la transmisión de datos, lo que lo convierte en un protocolo síncrono y full-duplex. Utiliza cuatro
líneas: MOSI (Master Out Slave In), MISO (Master In Slave Out), SCK (Serial Clock), y SS (Slave Select). SPI
permite una transferencia de datos rápida, ya que puede enviar y recibir información simultáneamente, lo que
lo hace ideal para aplicaciones que requieren alta velocidad. Sin embargo, su principal limitación es que no es
adecuado para comunicaciones con múltiples dispositivos, ya que cada uno necesita una línea de selección
(SS), lo que aumenta la complejidad en sistemas con muchos periféricos. A pesar de esto, SPI es ampliamente
utilizado en aplicaciones de sistemas embebidos, como la comunicación con tarjetas SD, pantallas LCD y
módulos de sensores, gracias a su eficiencia y velocidad.
Fig.2. Configuración protocolo SPI.
COMUNICACIÓN I2C
El protocolo I2C (Inter-Integrated Circuit) es un estándar de comunicación serial síncrona que utiliza dos líneas
compartidas, SDA (Serial Data) y SCL (Serial Clock), para transmitir datos entre un microcontrolador y varios
dispositivos periféricos. A través de estas líneas, el maestro genera el reloj y gestiona la comunicación, mientras
que los esclavos responden según sus direcciones únicas. I2C permite conectar múltiples dispositivos en el
mismo bus, lo que reduce la cantidad de pines necesarios. Aunque es más lento que otros protocolos como
SPI, su principal ventaja es la simplicidad y eficiencia en la conexión de varios dispositivos, lo que lo hace ideal
para aplicaciones como sensores, memorias y pantallas en sistemas embebidos. Sin embargo, puede presentar
limitaciones en cuanto a velocidad y manejo de muchos dispositivos en el bus.
Fig.3. Configuración protocolo I2C.
SENSOR GIROSCOPIO
Un giroscopio es un sensor que mide la velocidad angular o la tasa de rotación de un objeto en torno a sus ejes.
Su funcionamiento se basa en la conservación del momento angular, lo que significa que un objeto en rotación
tiende a mantener su orientación a menos que una fuerza externa lo altere. Los giroscopios modernos,
especialmente los MEMS (Microelectromechanical Systems), funcionan utilizando estructuras micromecánicas
que detectan las deformaciones causadas por la rotación, aprovechando el efecto Coriolis. Estas deformaciones
son medidas electrónicamente para calcular la velocidad angular. Los giroscopios pueden medir la rotación en
tres ejes (X, Y, Z), y sus salidas generalmente se expresan en grados por segundo o radianes por segundo.
Aunque son precisos, los giroscopios pueden sufrir deriva, que es un error acumulativo en las mediciones con
el tiempo.
Fig.4. Sensor de giroscopio integrado en placa STM32.
III. Metodología
El siguiente diagrama de bloques describe de manera general el funcionamiento del circuito
completo y el envío de la información entre protocolos y el microcontrolador:
Fig.5. Diagrama de bloques.
El siguiente diagrama de flujo describe el código utilizado en el software de MATLAB para graficar los
datos obtenidos mediante la comunicación de la computadora con el módulo bluetooth:
Fig.6. Diagrama de flujo MATLAB.
Durante la implementación del código se hizo uso de los códigos y librerías anexados en el campus
virtual como referencia para utilizar los protocolos de comunicación para esta práctica por lo que
tenemos los siguientes códigos de inicialización:
Protocolo UART
De acuerdo con la datasheet de la placa del microcontrolador STM32F401 se configuran los pines
PC6(TX) y PC7(RX) en modo alterno mediante el registro AFR.
Fig.7. Datasheet pines UART.
Para configurar estos pines en modo alterno es necesario utilizar el registro MODER, de acuerdo
con la siguiente tabla tenemos 0xA000 para configurarlos:
Fig.8. Tabla del registro MODER.
Después de configurar los pines del protocolo es necesario activar los siguientes registros para el
correcto funcionamiento de la comunicación con el microcontrolador:
USART6->CR1: Configura los parámetros básicos de la UART, como habilitarla (UE), habilitar la
recepción (RE) y transmisión (TE).
USART6->BRR: Define el baud rate (115200).
USART6->DR: Registra el dato a transmitir o recibe el dato entrante.
USART6->SR: Indica el estado de la UART, como si el registro de datos está vacío (TXE) o si hay
datos disponibles para leer (RXNE).
Protocolo SPI
Para este protocolo es necesario configurar los pines A5, A6, A7 y B10 para configurar el reloj, el
MISO, MOSI y el chip select del protocolo de comunicación respectivamente de acuerdo a la
datasheet del microcontrolador:
Fig.9. Datasheet pines SPI.
Después de configurar los pines del protocolo es necesario activar los siguientes registros para el
correcto funcionamiento de la comunicación con el microcontrolador:
SPI1->DR: Contiene el dato a enviar o el dato recibido.
SPI1->SR: Registra el estado del SPI, como si hay datos recibidos (RXNE) o si el SPI está ocupado
(BSY).
Protocolo I2C
Para el este protocolo de comunicación solo se necesitan dos pines de datos por lo que tenemos los
pines PB8 y PB9 configurados como modo alterno, confirmando así con la tabla del datasheet del
microcontrolador:
Fig.10. Datasheet pines I2C.
Después de configurar los pines del protocolo es necesario activar los siguientes registros para el
correcto funcionamiento de la comunicación con el microcontrolador:
I2C1->CR1: Controla la configuración principal del I2C, como habilitarlo (PE) y enviar condiciones
de inicio/parada (START, STOP).
I2C1->CR2: Configura la frecuencia del reloj I2C.
I2C1->SR1: Indica el estado del I2C, como si se ha generado un inicio (SB) o si un dato está listo
para ser enviado (TXE).
I2C1->SR2: Indica información adicional sobre el estado del bus, como si el I2C está ocupado
(BUSY).
I2C1->DR: Registra el dato a transmitir o el dato recibido.
I2C1->CCR: Configura el modo y la velocidad del I2C (estándar o rápido).
I2C1->TRISE: Configura el tiempo máximo de subida permitido para la señal.
Sensor Giroscopio LSM6DSO
De acuerdo con la datasheet de la placa de sensores de STMicroelectronics X-NUCLEO-IKS01A3
podemos observar que el sensor giroscopio es un LSM6DSO.
Fig.11. Sensores de la tarjeta X-NUCLEO-IKS01A3.
Después en la misma datasheet podremos encontrar la dirección del sensor que queremos utilizar
en este caso el LSM6DSO con la dirección D6, como se muestra en la siguiente imagen:
Fig.12. Dirección del sensor.
Mediante el registro CTRL2_G podemos configurar la frecuencia y estabilidad del sensor por lo que
se eligió la frecuencia más alta y una sensibilidad de 250 dps(degrees per second) lo que significa que
puede medir velocidades angulares de hasta 250 grados por segundo en un eje determinado. Esto
indica la capacidad máxima del sensor para registrar qué tan rápido está girando o rotando un objeto.
Fig.13. Frecuencia y estabilidad del sensor.
En la siguiente tabla podemos observar los valores del registro para cambiar la frecuencia del sensor
giroscopio, al utilizar la máxima frecuencia tenemos un valor de 0XA0:
Fig.14. Configuración de frecuencia.
En el datasheet del sensor LSM6DSO también podemos encontrar la dirección de los registros más
significativos y menos significativos de la cadena de bits que manda el sensor por lo que en este caso
al utilizar el eje X del giroscopio tendremos las deserciones 23 y 22 respectivamente.
Fig.15. MSB y LSB.
IV. Resultados
Al realizar las conexiones pertinentes es necesario revisar el pinout de cada modulo utilizado, al estar utilizando
elementos de la misma STMicroelectronics estos tienen facilidad para interconectarse entre ellos como los son
el microcontrolador y la pantalla TFT la cual solo se necesita ensamblar por la parte superior del
microcontrolador sin añadir conexiones externas, después integramos tanto el módulo de sensores de
STMicroelectronics en el cual tenemos el sensor giroscopio y el modulo bluetooth para el envió de datos a la
computadora.
Modulo Bluetooth
Placa
HC05 STMicroelectronics
con sensor de
giroscopio
Microcontrolador Pantalla TFT LCD
STM32F401RE
Fig.17. Armado del circuito.
Después de haber echo el armado del circuito es necesario conectar el modulo bluetooth a la computadora, se
carga el código a al microcontrolador y se corre el código generado en Matlab, de acuerdo a lo programado se
empezarán a mostrar los datos obtenidos del sensor hasta que la computadora reciba datos por medio del
módulo bluetooth, graficándolos en la pantalla LCD y mostrándolos en la gráfica de Matlab.
Fig.18. Inicio del código de Matlab.
Como se muestra en la imagen anterior no tenemos ningún dato graficado por lo que nuestro sensor
no ha detectado ningún cambio. Para poder observar valores procedemos a girar la placa de sensores
y obtuvimos lo siguiente:
Fig.19. Grafica de los datos obtenidos del sensor.
Al girar nuestro sensor este detecta los cambios bruscos de giro en el eje X por lo que solo en este
eje podremos notar las variaciones en el sensor, obteniendo la gráfica anterior, al no detectar cambios,
la señal regresa a un estado cero lo que significa que no hay movimiento alguno de giro.
Fig.20. Inicio del código de Matlab.
Fig.21. Grafica de los datos obtenidos del sensor.
La variación en las graficas obtenidas es debido a que las mediciones se hicieron en tiempo real por
lo que no se podía capturar los mismos gráficos de la computadora que de la pantalla TFT, pero de
acuerdo a que tan brusco se girara la placa de sensores en el eje X este cambiaba de amplitud y
regresaba a su estado cero comprobando así el correcto funcionamiento de las conexiones entre
protocolos de comunicación
V. Conclusión
Los protocolos de comunicación UART, SPI e I2C son esenciales para la integración de periféricos, así como
para la transmisión y adquisición de datos en sistemas electrónicos. Cada uno ofrece ventajas específicas que
lo hacen adecuado para diferentes necesidades. UART es simple y eficiente para conexiones de datos a larga
distancia, ideal para la comunicación entre microcontroladores y computadoras. SPI proporciona una mayor
velocidad de transmisión, siendo perfecto para aplicaciones que requieren rapidez y mayor rendimiento, aunque
con más líneas de conexión. Por su parte, I2C permite conectar varios dispositivos con solo dos cables, lo que
lo hace económico y fácil de implementar en sistemas con múltiples periféricos. La elección del protocolo
adecuado depende de las características del sistema, como la velocidad, la cantidad de dispositivos y la
distancia de transmisión, asegurando una comunicación eficaz y confiable.
VI. Referencias
[1] ¿Qué son y para qué sirven los protocolos de comunicación de redes? (s. f.). KIO.
[Link]
[2] Rohde & Schwarz. (s. f.). Entendiendo el UART. [Link]
[Link]/lat/productos/prueba-y-medicion/essentials-test-equipment/digital
oscilloscopes/entendiendo-el-uart_254524.html
[3] El bus SPI – Prometec. (s. f.). [Link]
spi/#:~:text=El%20SPI%20es%20un%20protocolo,la%20placa%20de%20circuito%20impreso.
[4] Paguayo. (2022, 25 agosto). Protocolo I2C (Inter Integrated Circuit) - MCI Educación. MCI
Educación. [Link]
VII. Códigos utilizados
Código STM32Cube IDE
#include "main.h"
#include "stdlib.h"
#define baud (uint16_t)(16.0e6/115200)
const unsigned short imagen[76800] = {};
// COMMAND DEFINITION
#define _height 320 ////TAMAÑO DE LA PANTALLA
#define _width 240
#define DC_HIGH GPIOB->ODR |= 0x400 ///pines DC y CS
#define CS_HIGH GPIOA->ODR |=0x200
#define DC_LOW GPIOB->ODR &= ~0x400
#define CS_LOW GPIOA->ODR &= ~0x200
// ---------------------------------------------------------------
#define ILI9341_NOP 0x00 // No Operation
#define ILI9341_SWRESET 0x01 // Software Reset
#define ILI9341_RDDIDIF 0x04 // Read Display Identification Information
#define ILI9341_RDDST 0x09 // Read Display Status
#define ILI9341_RDDPM 0x0A // Read Display Power Mode
#define ILI9341_RDDMADCTL 0x0B // Read Display MADCTL
#define ILI9341_RDDCOLMOD 0x0C // Read Display Pixel Format
#define ILI9341_RDDIM 0x0D // Read Display Image Format
#define ILI9341_RDDSM 0x0E // Read Display Signal Mode
#define ILI9341_RDDSDR 0x0F // Read Display Self Diagnostics Result
// ---------------------------------------------------------------
#define ILI9341_SLPIN 0x10 // Enter Sleep Mode
#define ILI9341_SLPOUT 0x11 // Sleep Out
#define ILI9341_PTLON 0x12 // Partial Mode On
#define ILI9341_NORON 0x13 // Normal Display On
// ---------------------------------------------------------------
#define ILI9341_DINVOFF 0x20 // Dislpay Inversion Off
#define ILI9341_DINVON 0x21 // Dislpay Inversion On
#define ILI9341_GAMSET 0x26 // Gamma Set
#define ILI9341_DISPOFF 0x28 // Display OFF
#define ILI9341_DISPON 0x29 // Display ON
#define ILI9341_CASET 0x2A // Column Address Set
#define ILI9341_PASET 0x2B // Page Address Set
#define ILI9341_RAMWR 0x2C // Memory Write
#define ILI9341_RGBSET 0x2D // Color Set
#define ILI9341_RAMRD 0x2E // Memory Read
// ---------------------------------------------------------------
#define ILI9341_PLTAR 0x30 // Partial Area
#define ILI9341_VSCRDEF 0x33 // Vertical Scroll Definition
#define ILI9341_TEOFF 0x34 // Tearing Effect Line OFF
#define ILI9341_TEON 0x35 // Tearing Effect Line ON
#define ILI9341_MADCTL 0x36 // Memory Access Control
#define ILI9341_VSSAD 0x37 // Vertical Scrolling Start Address
#define ILI9341_IDMOFF 0x38 // Idle Mode OFF
#define ILI9341_IDMON 0x39 // Idle Mode ON
#define ILI9341_COLMOD 0x3A // Pixel Format Set
#define ILI9341_WMCON 0x3C // Write Memory Continue
#define ILI9341_RMCON 0x3E // Read Memory Continue
// ---------------------------------------------------------------
#define ILI9341_IFMODE 0xB0 // RGB Interface Signal Control
#define ILI9341_FRMCRN1 0xB1 // Frame Control (In Normal Mode)
#define ILI9341_FRMCRN2 0xB2 // Frame Control (In Idle Mode)
#define ILI9341_FRMCRN3 0xB3 // Frame Control (In Partial Mode)
#define ILI9341_INVTR 0xB4 // Display Inversion Control
#define ILI9341_PRCTR 0xB5 // Blanking Porch Control
#define ILI9341_DISCTRL 0xB6 // Display Function Control
#define ILI9341_ETMOD 0xB7 // Entry Mode Set
#define ILI9341_BKCR1 0xB8 // Backlight Control 1
#define ILI9341_BKCR2 0xB9 // Backlight Control 2
#define ILI9341_BKCR3 0xBA // Backlight Control 3
#define ILI9341_BKCR4 0xBB // Backlight Control 4
#define ILI9341_BKCR5 0xBC // Backlight Control 5
#define ILI9341_BKCR7 0xBE // Backlight Control 7
#define ILI9341_BKCR8 0xBF // Backlight Control 8
// ---------------------------------------------------------------
#define ILI9341_PWCTRL1 0xC0 // Power Control 1
#define ILI9341_PWCTRL2 0xC1 // Power Control 2
#define ILI9341_VCCR1 0xC5 // VCOM Control 1
#define ILI9341_VCCR2 0xC7 // VCOM Control 2
// ---------------------------------------------------------------
#define ILI9341_RDID1 0xDA // Read ID1
#define ILI9341_RDID2 0xDB // Read ID2
#define ILI9341_RDID3 0xDC // Read ID3
// ---------------------------------------------------------------
#define ILI9341_GMCTRP1 0xE0 // Positive Gamma Correction
#define ILI9341_GMCTRN1 0xE1 // Neagtove Gamma Correction
// Extend register commands
// ---------------------------------------------------------------
#define ILI9341_LCD_POWERA 0xCB // Power control A register
#define ILI9341_LCD_POWERB 0xCF // Power control B register
#define ILI9341_LCD_DTCA 0xE8 // Driver timing control A
#define ILI9341_LCD_DTCB 0xEA // Driver timing control B
#define ILI9341_LCD_POWER_SEQ 0xED // Power on sequence register
#define ILI9341_LCD_3GAMMA_EN 0xF2 // 3 Gamma enable register
#define ILI9341_LCD_PRC 0xF7 // Pump ratio control register
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
uint8_t spi(uint8_t data){
uint8_t RX;
SPI1 -> DR = data;
while(!(SPI1->SR&0x01));
RX=SPI1->DR;
return RX;
}
// Función para leer múltiples registros
uint32_t read_registers(uint8_t add, uint8_t reg, uint8_t n){
uint32_t rx, x = n;
while(I2C1->SR2 & 0x10);
I2C1->CR1 &= ~0x800;
I2C1->CR1 |= 0x100;
while(!(I2C1->SR1 & 0x1));
I2C1->DR = add;
while(!(I2C1->SR1 & 0x2));
rx = I2C1->SR2 | I2C1->SR1;
while(!(I2C1->SR1 & 0x80));
I2C1->DR = reg;
while(!(I2C1->SR1 & 0x84));
I2C1->CR1 |= 0x500;
while(!(I2C1->SR1 & 0x1));
I2C1->DR = add | 0x1;
while(!(I2C1->SR1 & 0x2));
rx = I2C1->SR2 | I2C1->SR1;
rx = 0;
while(x){
while(!(I2C1->SR1 & 0x40));
rx |= ((uint32_t)I2C1->DR) << (8 * (n - x));
x--;
}
I2C1->CR1 &= ~0x400;
I2C1->CR1 |= 0x200;
return rx;
}
// Función para leer un solo registro
uint8_t read_register(uint8_t add, uint8_t reg){
uint8_t rx;
while(I2C1->SR2 & 0x10);
I2C1->CR1 &= ~0x800;
I2C1->CR1 |= 0x100;
while(!(I2C1->SR1 & 0x1));
I2C1->DR = add;
while(!(I2C1->SR1 & 0x2));
rx = I2C1->SR2 | I2C1->SR1;
while(!(I2C1->SR1 & 0x80));
I2C1->DR = reg;
while(!(I2C1->SR1 & 0x84));
I2C1->CR1 |= 0x500;
while(!(I2C1->SR1 & 0x1));
I2C1->DR = add | 0x1;
while(!(I2C1->SR1 & 0x2));
rx = I2C1->SR2 | I2C1->SR1;
while(!(I2C1->SR1 & 0x40));
rx = I2C1->DR;
I2C1->CR1 &= ~0x400;
I2C1->CR1 |= 0x200;
return rx;
}
// Función para escribir múltiples registros
void write_registers(uint8_t add, uint8_t reg, uint8_t *data, uint8_t n){
uint8_t x;
while(I2C1->SR2 & 0x10);
I2C1->CR1 &= ~0x800;
I2C1->CR1 |= 0x100;
while(!(I2C1->SR1 & 0x1));
I2C1->DR = add;
while(!(I2C1->SR1 & 0x2));
x = I2C1->SR2 | I2C1->SR1;
while(!(I2C1->SR1 & 0x80));
I2C1->DR = reg;
x = n;
while(x){
while(!(I2C1->SR1 & 0x80));
I2C1->DR = data[n - x];
x--;
}
while(!(I2C1->SR1 & 0x84));
I2C1->CR1 |= 0x200;
}
// Función para escribir un solo registro
uint8_t write_register(uint8_t add, uint8_t reg, uint8_t data){
uint8_t x;
while(I2C1->SR2 & 0x10);
I2C1->CR1 &= ~0x800;
I2C1->CR1 |= 0x100;
while(!(I2C1->SR1 & 0x1));
I2C1->DR = add;
while(!(I2C1->SR1 & 0x2));
x = I2C1->SR2 | I2C1->SR1;
while(!(I2C1->SR1 & 0x80));
I2C1->DR = reg;
while(!(I2C1->SR1 & 0x80));
I2C1->DR = data;
while(!(I2C1->SR1 & 0x84));
I2C1->CR1 |= 0x200;
return x;
}
void ILI9341_TransmitCmmd (uint8_t cmmd)
{
// D/C (wr) -> LOW
GPIOB->ODR &=~0x400;
// enable chip select -> LOW
GPIOA->ODR &=~0x200;
// set command on PORT
spi(cmmd);
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
// disable chip select -> HIGH
GPIOA->ODR |=0x200;
}
void ILI9341_Transmit8bitData (uint8_t data){
// D/C -> HIGH
GPIOB->ODR |=0x400;
// enable chip select -> LOW
GPIOA->ODR &=~0x200;
// set data on PORT
spi(data);
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
// disable chip select -> HIGH
GPIOA->ODR |=0x200;
}
void ILI9341_Transmit16bitData (uint16_t data){
// D/C -> HIGH
GPIOB->ODR |=0x400;
// enable chip select -> LOW
GPIOA->ODR &=~0x200;
spi((uint8_t) (data >> 8));
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
spi((uint8_t) data);
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
// disable chip select -> HIGH
GPIOA->ODR |=0x200;
}
void ILI9341_Transmit32bitData (uint32_t data){
// D/C -> HIGH
GPIOB->ODR |=0x400;
// enable chip select -> LOW
GPIOA->ODR &=~0x200;
spi((uint8_t) (data >> 24));
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
spi((uint8_t) (data >> 16));
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
spi((uint8_t) (data >> 8));
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
spi((uint8_t) data);
// Write impulse
GPIOB->ODR &=~0x400;
GPIOB->ODR |=0x400;
// disable chip select -> HIGH
GPIOA->ODR |=0x200;
}
void ILI9341_SetWindow (uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye){
// check if coordinates is out of range
if ((xs > xe) || (xe > 240) ||
(ys > ye) || (ye > 320))
{
// out of range
while(1);
}
// set column
ILI9341_TransmitCmmd(ILI9341_CASET);
// set column -> set column
ILI9341_Transmit32bitData(((uint32_t) xs << 16) | xe);
// set page
ILI9341_TransmitCmmd(ILI9341_PASET);
// set page -> high byte first
ILI9341_Transmit32bitData(((uint32_t) ys << 16) | ye);
// success
}
void delay(uint16_t time) {
RCC->APB1ENR |= 0x1;
TIM2->PSC = 8400 - 1; //1 ms
TIM2->ARR = time - 1;
TIM2->CNT = 0;
TIM2->CR1 |= 0x1; //Iniciar el temporizador;
while (!(TIM2->SR & 0x1));
TIM2->SR &= ~0x1;
TIM2->CR1 &= ~ 0x1;
}
void draw_pixel(int x, int y, uint16_t color) {
// Establecer la ventana con las mismas coordenadas de inicio y fin
ILI9341_SetWindow(x, y, x, y);
// Enviar el comando para escribir en la memoria de la pantalla
ILI9341_TransmitCmmd(ILI9341_RAMWR);
// Enviar el color (normalmente en 16 bits, divididos en dos bytes)
ILI9341_Transmit8bitData((color >> 8) & 0xFF); // Byte alto del color
ILI9341_Transmit8bitData(color & 0xFF); // Byte bajo del color
}
void draw_line(int x, int y_start, int y_end, uint16_t color) {
int y;
for (y = y_start; y <= y_end; y++) {
draw_pixel(x, y, color); // Dibuja cada píxel de la línea
}
}
int sensor(int valor, int scrmax, int resolution) {
return (valor*scrmax)/resolution;
}
void ILI9341_DrawSquare3x3(uint16_t x, uint16_t y, uint16_t color) {
// Definir ventana de 3x3 para el cuadrado
ILI9341_SetWindow(x, y, x + 2, y + 2);
// Activar la función de memoria de escritura
ILI9341_TransmitCmmd(ILI9341_RAMWR);
// Dibujar el cuadrado de 3x3 enviando el color 9 veces (3x3)
for (int i = 0; i < 9; i++) {
ILI9341_Transmit16bitData(color);
}
// Restablecer la ventana al tamaño completo
ILI9341_SetWindow(0, 0, _width - 1, _height - 1);
}
void ili9341 (void){
uint32_t i;
GPIOA->ODR |= 0x202;//HIGH reset wr cs
GPIOB->ODR |= 0x400;
GPIOA->ODR &=~0x2;//low reset
for(i=0;i<4000;i++);
GPIOA->ODR |=0x2;//high reset
for(i=0;i<80000;i++);
ILI9341_TransmitCmmd(ILI9341_SWRESET);
ILI9341_Transmit8bitData(0);
for(i=0;i<20000;i++);
ILI9341_TransmitCmmd(ILI9341_DISPOFF);
ILI9341_Transmit8bitData(0);
ILI9341_TransmitCmmd(ILI9341_PWCTRL1);
ILI9341_Transmit8bitData(0x23);
ILI9341_TransmitCmmd(ILI9341_PWCTRL2);
ILI9341_Transmit8bitData(0x10);
ILI9341_TransmitCmmd(ILI9341_VCCR1);
ILI9341_Transmit8bitData(0x2B);
ILI9341_Transmit8bitData(0x2B);
ILI9341_TransmitCmmd(ILI9341_VCCR2);
ILI9341_Transmit8bitData(0xC0);
ILI9341_TransmitCmmd(ILI9341_MADCTL);
ILI9341_Transmit8bitData(0x88);//48
ILI9341_TransmitCmmd(ILI9341_COLMOD);
ILI9341_Transmit8bitData(0x55);
ILI9341_TransmitCmmd(ILI9341_FRMCRN1);
ILI9341_Transmit16bitData(0x001B);
//ILI9341_Transmit8bitData(0x1B);
ILI9341_TransmitCmmd(ILI9341_ETMOD);
ILI9341_Transmit8bitData(0x07);
ILI9341_TransmitCmmd(ILI9341_SLPOUT);
ILI9341_Transmit8bitData(0);
for(i=0;i<60000;i++);
ILI9341_TransmitCmmd(ILI9341_DISPON);
ILI9341_Transmit8bitData(0);
for(i=0;i<80000;i++);
ILI9341_SetWindow(0, 0, 239, 319);
}
void draw_line_bresenham(int x1, int y1, int x2, int y2, uint16_t color) {
int dx = abs(x2 - x1); // Diferencia en la dirección x
int dy = abs(y2 - y1); // Diferencia en la dirección y
int sx = (x1 < x2) ? 1 : -1; // Dirección en x
int sy = (y1 < y2) ? 1 : -1; // Dirección en y
int err = dx - dy; // Error acumulado
while (1) {
draw_pixel(x1, y1, color); // Dibuja el píxel en la posición (x1, y1)
if (x1 == x2 && y1 == y2) {
break; // Si hemos llegado al punto final, terminamos
}
int e2 = err * 2;
if (e2 > -dy) {
err -= dy;
x1 += sx; // Mueve el punto en x
}
if (e2 < dx) {
err += dx;
y1 += sy; // Mueve el punto en y
}
}
}
void Uart2Config() {
USART6->CR1 = 0x00; // Limpiar configuración
USART6->CR1 |= (1 << 13);
USART6->BRR = baud;
USART6->CR1 |= (1 << 2);
USART6->CR1 |= (1 << 3);
}
void UART2_SendChar(char c) {
USART6->DR = c; // Cargar el dato
while (!(USART6->SR & (1 << 7))); // Esperar que la transmisión finalice (TC)
}
void UART2_SendString(char *str) {
char ch;
while ((ch = *(str++))){
UART2_SendChar(ch);
}
UART2_SendChar(0xD);
UART2_SendChar(0xA);
}
int main (void){
int new_y;
int valor;
int sx = 30;
int AX=30;
int AY=30;
// Configuración de los relojes
RCC->APB1ENR |= 0x10200000;
while(!(RCC->APB1ENR & 0x200000));
RCC->AHB1ENR |= 0x3;
while(!(RCC->AHB1ENR & 0x3));
// Configuración de los pines para I2C
GPIOB->MODER |= 0xA0000;
GPIOB->OTYPER |= 0x300;
GPIOB->OSPEEDR |= 0xF0000;
GPIOB->AFR[1] |= 0x44;
// Inicialización del I2C
I2C1->CR1 |= 0x8000;
I2C1->CR1 &= ~0x8000;
I2C1->CR2 |= 0x14;
I2C1->TRISE = 0x11;
I2C1->CCR |= 0x50;
I2C1->OAR1 |= 0x4000;
I2C1->CR1 |= 0x1;
write_register(0xD6, 0x11, 0xA0); //Direccion de sensor,Dirección de configuración del sensor,
Configuración del sensor
//read_register(0xD6,0x0F); //Función WHO A MY
//Dirección del sensor, Registro que se lee
uint8_t G_low, G_high;
int16_t G_acc;
//RCC->APB1ENR |= 0x10000000;
while(!(RCC->APB1ENR&0x10000000));
RCC->APB2ENR |= 0x1000;
while(!(RCC->APB2ENR&0x1000));
RCC->AHB1ENR |= 0x3;
while(!(RCC->AHB1ENR&0x3));
GPIOA->MODER |=0x4A804;
GPIOA->OSPEEDR |=0x4A804;
GPIOA->AFR[0] |=0x55500000;
GPIOB->MODER |= 0x100000;
GPIOB->OSPEEDR |= 0x200000;
SPI1->CR1 |= 0x344;
ili9341 ();
ILI9341_TransmitCmmd(ILI9341_RAMWR);
uint32_t i;
for(i=0;i<76800;i++)
ILI9341_Transmit16bitData(imagen[i]);
//draw_pixel(150, 150, RED);
//draw_line(50, 20, _height, RED);
RCC->APB2ENR |= 0x0020;
RCC->AHB1ENR |= 0x4; // GPIOCEN está en el bit 2 (0x4) de AHB1
// Configurar PC6 y PC7 como modo alternativo
GPIOC->MODER &= 0xFFFCFFF; // Limpiar configuración actual de PC6 y PC7
GPIOC->MODER |= 0x000A000; // Configurar PC6 y PC7 como modo alternativo (10 en binario)
// Configurar PC6 y PC7 para la función alternativa AF8 (USART6)
GPIOC->AFR[0] &= 0x00FFFFFF; // Limpiar función alternativa de PC6 y PC7
GPIOC->AFR[0] |= 0x88000000; // Configurar PC6 y PC7 en AF8 (USART6)
// Llamar a la función de configuración de UART6 (ajusta la configuración de UART6 según lo
necesites)
Uart2Config();
while(!(USART6->SR & USART_SR_RXNE));
while(1){
G_low = read_register(0xD6, 0x22);
G_high = read_register(0xD6, 0x23);
G_acc = (G_high << 8) | G_low; //valor de 16 bits
//sección para enviar las cadenas de 8 bits
//UART2_SendChar(G_low);
UART2_SendChar(G_high);
valor = G_acc;
new_y = sensor(valor+30, 290, 32818);
if(new_y <30){
new_y=30;
}
if(new_y > 320){
new_y =320;
}
draw_line(sx, 30, 320, WHITE);
//draw_pixel(x, new_y, RED);
draw_line_bresenham(AX,AY,sx,new_y,BLUE);
AX=sx;
AY=new_y;
sx++;
if (sx >= 219) {
sx = 30;
AX=30;
AY=30;
}
delay(2);
}
}
Código Matlab
Clc; clear; close all
%Eliminar puerto anterior
delete(instrfind(('Port'),('COM15')));
%Crear conexión serie
s = serialport ('COM15', 115200);
altura = 1000;
% Variables de inicialización para la gráfica
figure;
hold on;
grid on;
title('Datos en tiempo real desde el sensor');
xlabel('Tiempo (s)');
ylabel('Valor del Sensor');
xlim([0 1000]); % Rango de tiempo (puedes ajustarlo según el tiempo de muestreo)
ylim([0 altura]); % Define 'altura' como el valor máximo para tu sensor
set(gca, 'YDir', 'reverse'); % Invertir el eje Y para que 0 esté en la parte inferior
tic; % Para llevar control del tiempo
dataX = []; % Vector para el tiempo
dataY = []; % Vector para los datos del sensor
%write(s,'1','uint8');
[Link];
write(s,'1','uint8');
% Bucle de adquisición y visualización de datos
while ishandle(gca) % Mantiene el bucle activo mientras la figura esté abierta
% Leer datos del sensor del puerto serial
if [Link] >= 2
% Lee dos bytes (como en tu código de microcontrolador)
a_low = read(s, 1, 'uint8'); % Leer el byte bajo
a_high = read(s, 1, 'uint8'); % Leer el byte alto
a_combinada = bitor(bitshift(a_high, 8), a_low); % Combina en 16 bits
% Conversión y escalado del valor leído
valor = a_combinada;
if bitget(a_combinada,16) == 1
a_combinada = a_combinada - 2^16;
end
y_actual = (a_combinada * altura) / (32767);
% Agregar los valores actuales a los datos de la gráfica
dataX(end+1) = toc; % Tiempo actual
dataY(end+1) = y_actual;
% Actualizar la gráfica en tiempo real
plot(dataX, dataY, 'b-');
drawnow; % Refresca la gráfica sin delay
% Mantener el tamaño de la gráfica en pantalla (ajuste de límites)
if length(dataX) > 100 % Ajusta según el número de puntos que deseas mostrar
xlim([dataX(end) - 10, dataX(end)]);
end
end
end
% Cerrar la conexión serial cuando se cierre la figura
clc
clear
close all
clear s;