Instituto Politécnico Nacional
Unidad Profesional Interdisciplinaria de
Ingeniería Campus Zacatecas
Introducción a los Microcontroladores
Investigación 5
3CM3
Mtro. Adán Orenday Delgado
Aranza Miranda Montellano
Fecha de entrega:
07/06/2021
PROTOCOLOS DEL ATMEGA328P
I2C)
I2C es un protocolo síncrono. I2C usa solo 2 cables, uno para el reloj (SCL) y otro
para el dato (SDA). Esto significa que el maestro y el esclavo envían datos por el
mismo cable, el cual es controlado por el maestro, que crea la señal de reloj. I2C
no utiliza selección de esclavo, sino direccionamiento.
I2C es un bus de comunicaciones en serie. Su nombre viene de Inter-Integrated
Circuit (Inter-Circuitos Integrados). La versión 1.0 data del año 1992 y la versión
2.1 del año 2000, su diseñador es Philips. La velocidad es de 100 kbit/s en el
modo estándar, aunque también permite velocidades de 3.4 Mbit/s. Es un bus muy
usado en la industria, principalmente para comunicar microcontroladores y sus
periféricos en sistemas integrados (Embedded Systems) y generalizando más
para comunicar circuitos integrados entre si que normalmente residen en un
mismo circuito impreso.
La principal característica de I²C es que utiliza dos líneas para transmitir la
información: una para los datos y otra para la señal de reloj. También es necesaria
una tercera línea, pero esta sólo es la referencia (masa). Como suelen
comunicarse circuitos en una misma placa que comparten una misma masa esta
tercera línea no suele ser necesaria.
Configuración de un registro
Ejemplo: Para diseñar registrador de datos, es necesario en algunas
circunstancias usar un reloj de tiempo Real, RTC. Un RTC común es el DS1307
que tiene un puerto para poder leer y configurar el tiempo. La Figura-2, muestra la
trama de datos necesaria para configurar la hora para que sean las 08 hrs. Para
poder encontrar el sensor se requiere conocer su dirección. Para determinar la
dirección se tienen 7 bits ( 2^7 = 128 ), de aquí que se puedan comunicar con dos
líneas hasta 127 sensores, la dirección 0 es una llamada general.
Después se requiere un bit de lectura o escritura. Entonces, este bit acompaña a
los 7-bits de dirección. Si el bit de lectura/escritura vale = 0, significa que se
escribirá al esclavo. Si este bit vale = 1, significa entonces que se leerá. Cuando el
Esclavo I2C recibe su dirección, es cuando esté, responde con una confirmación
(ACK). El esclavo queda en espera de 8 bits de la memoria que se quiere escribir
y responde con un ACK. Posteriormente el esclavo espera a que el maestro le
envié los 8-bits de datos correspondientes a la configuración del registro anterior y
responde con un ACK. Finalmente, el Maestro-I2C responde con un bit de fin de
comunicación.
Ejemplo en lenguaje C:
Un proceso de temperatura tiene incorporado una tarjeta electrónica encargada de
realizar el monitoramiento de la variable temperatura en el interior del proceso.
Dicha tarjeta electrónica cuenta con PIC16F887, un LCD, un puerto de
comunicación serial, un sensor I2C de temperatura (DS1621), cuenta con un reloj
calendario de tiempo real I2C (DS1307) y cuenta además con una memoria
EEPROM serie I2C (M24512).
Se solicita al ingeniero de control realizar un programa en el PIC16F887, para que,
por medio de un menu en el computador, el usuario pueda tener la opción de leer
la temperatura y la hora de lectura de la misma almacenando estos datos en la
memoria EEPROM y que estos datos también se visualicen en el LCD.
El Menú debe tener una segunda opción que le permita al usuario ver por el puerto
serial los últimos datos almacenados en la EEPROM.
Programa principal:
#INCLUDE <16F887.h>
#DEVICE ADC=10
#USE DELAY(CLOCK=4000000)
#FUSES XT,NOPROTECT,NOWDT,NOBROWNOUT,PUT,NOLVP
//Configura direccion de memoria de los puertos A,B,C,D
#BYTE PORTA= 5
#BYTE PORTB= 6
#BYTE PORTC= 7
#BYTE PORTD= 8
#define EEPROM_SDA PIN_C4
#define EEPROM_SCL PIN_C3
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N)
#use i2c(MASTER,Fast=100000, sda=EEPROM_SDA, scl=EEPROM_SCL,force_sw)
#include <lcd.c>
#include <EEPROM_24512.c>
#include <DS1307.c>
#include <TEMP_DS1621.c>
#include <floatee.c>
int dat_in=0, cnt, hr, min, sec;
int16 address=0;
int dat_serie[7];
float dato=0;
//Interrupción por el puerto serial
#int_rda
void rda_isr(void)
{
dat_in=getc(); //Lee el puerto Serial
printf("%c\r",dat_in);
if (dat_in=='2') //Pregunta si es "2" para visualizar parametros de la EEPROM
{
for(cnt=address-7;cnt<=address;cnt++) //Lee los primeros 7 bytes de la EEPROM
{
dat_serie[cnt]=read_ext_eeprom(cnt);
printf("Byte %u=%3u \r", cnt, dat_serie[cnt]);
}
}
}
void main()
{
lcd_init();
set_tris_c(255);
enable_interrupts(int_rda);
enable_interrupts(global);
address=0;
init_temp(0x03); //Inicializa el DS1621
//Crea un Menu para mostrar por el Serial
printf("Pulse 1 para leer los datos\r");
printf("Pulse 2 para visualizar los datos\r");
while(1)
{
if(dat_in=='1') //Si es "1" inicie la lectura y grabado de la EEPROM
{
init_temp(0x03); //Inicializa el DS1621
rtc_get_time(hr,min,sec); //Lee el tiempo del DS1307
dato=read_full_temp(0x03); //Lee temperatura del DS1621
write_float_ext_eeprom(address,dato); //Guarda 4 bytes del float
address=address+4;
write_ext_eeprom(address++,hr); //Guarda byte de Hora
write_ext_eeprom(address++,min); //Guarda byte de Minuto
write_ext_eeprom(address++,sec); //Guarda byte de Segundo
lcd_gotoxy(1,1);
printf(lcd_putc,"Temp=%4.1f C \n",dato);//Visualiza la temperatura
printf(lcd_putc,"%2u:%2u:%2u",hr,min,sec);//Visualiza la hora
if (address==0xffff)
address=0; //Cuando se termina la EEPROM vuelve al principio
dat_in=0;
}
}
}
SPI)
La SPI fue desarrollada por Motorola (ahora parte de NXP Semiconductors)
aproximadamente en 1985. Se trata de una interfaz serial síncrona prevista para la
comunicación entre dispositivos a corta distancia. Desde entonces, se ha
convertido en un estándar de-facto empleado por muchos fabricantes de
semiconductores, especialmente en microprocesadores y microcontroladores.
El motivo de la popularidad de SPI radica en sus muchas ventajas. La primera es
que es una interfaz direccionada de hardware simple que ofrece completa
flexibilidad para la cantidad de bis transferidos. Usa un modelo de maestro-
secundario con un maestro simple y puede manejar varios dispositivos
secundarios usando comunicaciones dúplex que operan a velocidades de reloj de
hasta 50 MHz. No usa un protocolo estándar y transfiere solo paquetes de datos,
lo que la hace ideal para transferir flujos de datos largos.
SPI usa un máximo de cuatro líneas de señal (Figura 1). El dispositivo maestro,
por lo general un procesador o controlador, suministra y controla el reloj (SCK) y
líneas de selección de chip (CS). La operación multiplexor completa se maneja a
través de las líneas de datos Master Out Slave In (MOSI) y Master In Slave Out
(MISO). En un maestro individual simple, con configuración del dispositivo
secundario individual, la línea de selección de chip puede eliminarse y se puede
forzar la entrada de CS al dispositivo secundario al estado lógico habilitado. Si el
dispositivo secundario solo puede enviar datos (comunicación semidúplex), luego
la línea MOSI también puede eliminarse, y así reducir el conteo de señales
adicionalmente. Los datos salen a través de la señal del reloj de tal forma que la
transferencia de datos se asemeja a un registro de turnos con un bit cambiado
para cada reloj.
Internamente el microcontrolador ATmega328p tiene un bus SPI que puede
trabajar como master o slave. Para manejar internamente este bus, se hace uso
de una serie de registros.
Características del bus SPI en Arduino:
Full-duplex, Three-wire Synchronous Data Transfer
Master or Slave Operation
LSB First or MSB First Data Transfer
Seven Programmable Bit Rates
End of Transmission Interrupt Flag
Write Collision Flag Protection
Wake-up from Idle Mode
Double Speed (CK/2) Master SPI Mode
Los microcontroladores AVR tienen los siguientes tres registros para manejar SPI:
SPCR – SPI Control Register – This register is basically the master register
i.e. it contains the bits to initialize SPI and control it.
SPSR – SPI Status Register – This is the status register. This register is
used to read the status of the bus lines.
SPDR – SPI Data Register – The SPI Data Register is the read/write
register where the actual data transfer takes place.
Ejemplo:
Sensor de presión
Código:
#include <SPI.h>
//Sensor's memory register addresses:
const int PRESSURE = 0x1F; //3 most significant bits of pressure
const int PRESSURE_LSB = 0x20; //16 least significant bits of pressure
const int TEMPERATURE = 0x21; //16 bit temperature reading
const byte READ = 0b11111100; // SCP1000's read command
const byte WRITE = 0b00000010; // SCP1000's write command
// pins used for the connection with the sensor
// the other you need are controlled by the SPI library):
const int dataReadyPin = 6;
const int chipSelectPin = 7;
void setup() {
[Link](9600);
// start the SPI library:
[Link]();
// initalize the data ready and chip select pins:
pinMode(dataReadyPin, INPUT);
pinMode(chipSelectPin, OUTPUT);
//Configure SCP1000 for low noise configuration:
writeRegister(0x02, 0x2D);
writeRegister(0x01, 0x03);
writeRegister(0x03, 0x02);
// give the sensor time to set up:
delay(100);
}
void loop() {
//Select High Resolution Mode
writeRegister(0x03, 0x0A);
// don't do anything until the data ready pin is high:
if (digitalRead(dataReadyPin) == HIGH) {
//Read the temperature data
int tempData = readRegister(0x21, 2);
// convert the temperature to celsius and display it:
float realTemp = (float)tempData / 20.0;
[Link]("Temp[C]=");
[Link](realTemp);
//Read the pressure data highest 3 bits:
byte pressure_data_high = readRegister(0x1F, 1);
pressure_data_high &= 0b00000111; //you only needs bits 2 to 0
//Read the pressure data lower 16 bits:
unsigned int pressure_data_low = readRegister(0x20, 2);
//combine the two parts into one 19-bit number:
long pressure = ((pressure_data_high << 16) | pressure_data_low) / 4;
// display the temperature:
[Link]("\tPressure [Pa]=" + String(pressure));
}
}
//Read from or write to register from the SCP1000:
unsigned int readRegister(byte thisRegister, int bytesToRead) {
byte inByte = 0; // incoming byte from the SPI
unsigned int result = 0; // result to return
[Link](thisRegister, BIN);
[Link]("\t");
// SCP1000 expects the register name in the upper 6 bits
// of the byte. So shift the bits left by two bits:
thisRegister = thisRegister << 2;
// now combine the address and the command into one byte
byte dataToSend = thisRegister & READ;
[Link](thisRegister, BIN);
// take the chip select low to select the device:
digitalWrite(chipSelectPin, LOW);
// send the device the register you want to read:
[Link](dataToSend);
// send a value of 0 to read the first byte returned:
result = [Link](0x00);
// decrement the number of bytes left to read:
bytesToRead--;
// if you still have another byte to read:
if (bytesToRead > 0) {
// shift the first byte left, then get the second byte:
result = result << 8;
inByte = [Link](0x00);
// combine the byte you just got with the previous one:
result = result | inByte;
// decrement the number of bytes left to read:
bytesToRead--;
}
// take the chip select high to de-select:
digitalWrite(chipSelectPin, HIGH);
// return the result:
return (result);
}
//Sends a write command to SCP1000
void writeRegister(byte thisRegister, byte thisValue) {
// SCP1000 expects the register address in the upper 6 bits
// of the byte. So shift the bits left by two bits:
thisRegister = thisRegister << 2;
// now combine the register address and the command into one byte:
byte dataToSend = thisRegister | WRITE;
// take the chip select low to select the device:
digitalWrite(chipSelectPin, LOW);
[Link](dataToSend); //Send register location
[Link](thisValue); //Send value to record into register
// take the chip select high to de-select:
digitalWrite(chipSelectPin, HIGH);
}
Bibliografía:
J. (2017, 7 septiembre). I2C. Aprendiendo Arduino.
[Link]
“I2C – Puerto, Introducción, trama y protocol”. Hetpro. [Link]
[Link]/TUTORIALES/i2c/
Comunicación I2C PIC Explicación Fácil - [mayo, 2021]. (2020, 8
septiembre). Control Automático Educación.
[Link]
nicacion-i2c/
J. (2016, 15 noviembre). Bus SPI. Aprendiendo Arduino.
[Link]