Está en la página 1de 12

Escuela de Ingeniería.

IMEC

Microcontroladores y sistemas embebidos

Salón 28109

Práctica II

Presenta:

Marco Julio Reyes Ruiz #033171

Francisco Rosales #034052

Gustavo Martínez #034720

Hibram Hermosillo #391145

Mexicali, Baja California, a 21 de Mayo del 2022.


Objetivo
Programación de ARDUINO en AVR C. Diseño de proyectos que requieren la configuración
de entradas y salidas.
Introducción
Durante esta práctica en el curso de microcontroladores y sistemas embebidos, llevamos a cabo
dos etapas distintas. La primera de ellas consistió en diseñar un circuito que lea un teclado 4x4 y
ponga el resultado en un display de 7 segmentos. La segunda etapa, procedimos a programar un
circuito donde se utlizo un display de 7 segmentos del mismo tipo (cátodo comun) que despliegue
un numero de tres dígitos, estos estando conectados a un mismo bus de datos concetados al
PORTD.
Marco Teórico
Arquitectura de los microcontroladores AVR.
La arquitectura del AVR es una variante de la Harvard, la cual se caracteriza por tener dos
sistemas de memoria físicamente separados para almacenar los programas y los datos. Estas
memorias aparecen en espacios de direcciones distintos, pero el AVR tiene la habilidad de
acceder a los datos de la memoria del programa utilizando instrucciones específicas. Es decir,
el programa y los datos se encuentran en espacios de memoria diferentes, pero pueden ser
leídos por el procesador utilizando instrucciones especiales.
• Programa de memoria
En las MCU, las instrucciones del programa se almacenan en una memoria flash que es
no volátil, es decir, que no se pierde la información al apagar la energía. Aunque estas
MCU son de 8 bits, cada instrucción ocupa una o dos palabras de 16 bits. La capacidad
de la memoria del programa suele estar indicada en el nombre del dispositivo, por
ejemplo, la línea ATmega64x dispone de 64 KB de memoria flash, mientras que la línea
ATmega32x tiene 32 KB.
• Memoria de datos interna
El espacio de direcciones de datos se compone de varios elementos, tales como el
archivo de registro, los registros de entrada/salida (E/S) y la memoria SRAM. En
algunos modelos más pequeños, también se asigna la ROM del programa al espacio de
direcciones de datos, aunque esto no ocurre en los modelos más grandes.
• Registros internos.
Los dispositivos AVR son clasificados como RISC de 8 bits y cuentan con 32 registros
de un solo byte. En las versiones tinyAVR y megaAVR de la arquitectura AVR, los
registros de trabajo se encuentran en las primeras 32 direcciones de memoria (0000 16
–001F 16), seguidos por 64 registros de entrada/salida (E/S) (0020 16 –005F 16). En
los dispositivos que tienen múltiples periféricos, estos registros son seguidos por 160
registros de "E/S extendidas", los cuales solo son accesibles mediante la asignación de
memoria como E/S (0060 16 –00FF 16).
• Puertos GPIO.
Cada puerto GPIO en un AVR pequeño o mega maneja hasta ocho pines y está
controlado por tres registros de 8 bits: DDR x, PORT x y PIN x, donde x es el
identificador del puerto.
• EEPROM.
La mayoría de los microcontroladores AVR cuentan con una EEPROM interna para
almacenamiento de datos semipermanente. Al igual que la memoria flash, la EEPROM
es capaz de retener su contenido, aunque se interrumpa el suministro eléctrico.
Lenguaje C
En la década de los 70, Brian Kernighan y Dennis Ritchie crearon el lenguaje de programación
C. La primera implementación de este lenguaje fue realizada por Dennis Ritchie en un
computador DEC PDP-11 con el sistema operativo UNIX. El lenguaje C es el resultado de un
proceso de desarrollo que comenzó con un lenguaje anterior, el BCPL, el cual influyó en el
desarrollo de un lenguaje llamado B por parte de Ken Thompson. Este último lenguaje es el
antecedente directo del lenguaje C.
El lenguaje C es estructurado, al igual que otros lenguajes de programación como Pascal, Ada
o Modula-2. Sin embargo, a diferencia de estos lenguajes, no es estructurado por bloques, lo
que significa que no es posible declarar subrutinas dentro de otras subrutinas. Además, el
lenguaje C no es riguroso en la comprobación de tipos de datos, lo que permite una fácil
conversión entre diferentes tipos de datos y asignaciones entre tipos de datos diferentes.
En términos generales, un programa de C está compuesto por un conjunto de funciones,
incluyendo una función llamada main, que es la primera en ejecutarse al inicio del programa.
A partir de esta función, se llaman al resto de las funciones que componen el programa.
Lenguaje C (estructura en AVR studio vs estructura en ARDUINO IDE).
En la plataforma de Arduino IDE, no se pueden acceder completamente a los módulos que
conforman el hardware. En contraste con otros editores, esta plataforma no cuenta con las
herramientas necesarias para aprovechar cada uno de los componentes. En este sentido, Atmel
Studio/AVR Studio es un IDE utilizado por profesionales que permite desarrollar programas
en C, C++ y ensamblador para casi todos los microcontroladores Atmel. Debido a que la
estructura del lenguaje de programación de Arduino se basa en el lenguaje C, es posible cargar
estos archivos en los editores de AVR, aunque no es lo más adecuado.

Lenguaje C (estructura en AVR studio vs estructura en ARDUINO IDE).


En la plataforma de Arduino IDE, no se pueden acceder completamente a los módulos que
conforman el hardware. En contraste con otros editores, esta plataforma no cuenta con las
herramientas necesarias para aprovechar cada uno de los componentes. En este sentido, Atmel
Studio/AVR Studio es un IDE utilizado por profesionales que permite desarrollar programas
en C, C++ y ensamblador para casi todos los microcontroladores Atmel. Debido a que la
estructura del lenguaje de programación de Arduino se basa en el lenguaje C, es posible cargar
estos archivos en los editores de AVR, aunque no es lo más adecuado.
Desarrollo de la practica
Parte 1: Diseñar un circuito que lea un teclado de 4x4 etc.

{
#define F_CPU 16000000UL output_ddr=output_ddr&~((1<<0)|(1<<1)|(1<<2)|(1<<
#include <avr/io.h> 3)|(1<<4)|(1<<5));

#include <util/delay.h> key_ddr = key_ddr|(1<<4)|(1<<5)|(1<<6)|(1<<7);

#define key_port PORTD key_ddr = key_ddr&~((1<<0)|(1<<1)|(1<<2)|(1<<3));

#define key_ddr DDRD key_port = key_port|(1<<0)|(1<<1)|(1<<2)|(1<<3);

#define key_pin PIND unsigned char fila, colum;

#define output_ddr DDRB


#define output_port PORTB while (1)

unsigned char keypad[4][4]= {


{{0x00,0x01,0x02,0x03},{0x04,0x05,0x06,0x07}, //rutina para detectar que las teclas no esten
presionadas (PD3..PD0 todas en 1)
{0x08,0x09,0x0A,0x0B},{0x0C,0x0D,0x0E,0x0F}}; do
{
key_port&=0x0F; //pone
int main(void) PD7..PD4 en 0
colum=key_pin&0x0F; //
if (colum==0x0E) //0b 0000 1110
colum=key_pin&0x0F; // colloc<--[PD3..PD0] {
} while (colum!=0x0F); //repite la rutina
output_port=(output_port&0b11000000)|(keypad[fila
mientras alguna entrada PD3..PD0 sea 0
][0]&0x0F);

//poner los 4 bits menos significativos de keypad


//Rutina para reducir rebotes en output_prt
do }
{ else
do //detecta que se presione una tecla if (colum==0x0D) //0b 0000 1101
{ {
colum=key_pin&0x0F; //colloc<--[PD3..PD0]
output_port=(output_port&0b11000000)|(keypad[fila
} while (colum==0x0F); //repite la rutina ][1]&0x0F);
mientras PD3..PD0=1111
//poner los 4 bits menos significativos de keypad
_delay_ms(25); en output_prt
colum=key_pin&0x0F; //vuelve a leer las entradas }
colloc<--[PD3..PD0]
else
} while (colum==0x0F); //repite la rutina
principal mientras PD3..PD0=1111 if (colum==0x0B) //0b 0000 1011

{
//Rutina para hacer corrimientos e identificar output_port=(output_port&0b11000000)|(keypad[fila
Renglon y columna ][2]&0x0F);
while (1) //poner los 4 bits menos significativos de keypad
{ //renglon 0 en output_prt

key_port=(key_port&0x0F)|0b11100000; //pone en 0 }
renglon 0 ([PD7..PD4]=1110)
else
colum=key_pin&0X0F; //colloc<--[PD3..PD0]
//0b 0000 0111
if (colum!=0x0F) //si es verdadero quiere decir
que una tecla de ese renglon esta presionada output_port=(output_port&0b11000000)|(keypad[fila
][3]&0x0F);
{
//poner los 4 bits menos significativos de keypad
fila=0; en output_prt
break;
} }
//renglon 1 }
key_port=(key_port&0x0F)|0b11010000; //pone en 0
renglon 1 ([PD7..PD4]=1101)
colum=key_pin&0X0F;
//colloc<--[PD3..PD0]
if (colum!=0x0F) //si es
verdadero quiere decir que una tecla de ese renglon
esta presionada
{
fila=1;
break;
}
Descripción de código.
1. Configuración de los pines y constantes: Se definen algunas constantes y se incluyen las
bibliotecas necesarias para el control del microcontrolador AVR. Además, se definen las
asignaciones de los puertos para los pines utilizados en el teclado y la salida.

2. Configuración de los pines de entrada/salida: Se configuran los pines del


microcontrolador como entradas o salidas según sea necesario para el teclado y la salida.
Los pines PD4 a PD7 se configuran como salidas para controlar las filas del teclado, y los
pines PD0 a PD3 se configuran como entradas para leer las columnas del teclado.

3. Bucle principal: El programa entra en un bucle infinito para verificar constantemente si


se ha presionado una tecla en el teclado.

4. Rutina de detección de teclas no presionadas: Primero, se verifica si todas las teclas del
teclado están en estado no presionado. Esto se hace mediante un bucle que pone en 0 los
pines PD7 a PD4 y luego lee el estado de las columnas (PD3 a PD0). Si alguna columna
tiene un valor diferente de 0x0F (todos los bits en alto), significa que al menos una tecla
está presionada.

5. Rutina para reducir rebotes: Una vez que se detecta que al menos una tecla está
presionada, se realiza una rutina para reducir los rebotes que pueden ocurrir debido a la
fluctuación en el estado de la tecla. Se utiliza otro bucle que espera hasta que se presione
una tecla y luego espera un breve período de tiempo (25 ms). Luego, se lee nuevamente
el estado de las columnas para confirmar que la tecla todavía está presionada.

6. Rutina para identificar fila y columna: Después de confirmar que se ha presionado una
tecla y reducir los rebotes, se utiliza un bucle para determinar la fila y la columna
correspondientes a la tecla presionada. El bucle recorre las filas (0 a 3) y configura cada
una en 0 mientras lee el estado de las columnas. Si encuentra una columna que no está en
estado alto (0x0F), significa que se ha presionado una tecla en esa fila.

7. Asignación de salida: Una vez que se determina la fila y la columna de la tecla


presionada, se actualiza el valor de salida en el puerto B del microcontrolador.
Dependiendo de la columna detectada, se selecciona el valor correspondiente de la matriz
"keypad" y se coloca en los 4 bits menos significativos del puerto B.

8. El bucle principal se repite continuamente, lo que permite detectar y procesar las teclas
presionadas en el teclado matricial.
Desarrollo de la practica
Parte 2: Programar un circuito donde se utilice un display de 7 segmentos etc.

#define F_CPU 16000000UL 0b01111001, //E

#include <avr/io.h> 0b01110001 //F

#include <util/delay.h> };

resultado = binario[x];

#define SegOne_ON (PORTB|= 0b00000001) return resultado;

#define SegTwo_ON (PORTB|= 0b00000010) }

#define SegThree_ON (PORTB|= 0b00000100)

#define SegOne_OFF (PORTB&= 0b11111110) uint8_t seg1 = 0, seg2 = 0, seg3 = 0, a = 0;

#define SegTwo_OFF (PORTB&= 0b11111101) signed long num = 0;

#define SegThree_OFF (PORTB&= 0b11111011)


int MUX(void){

//despliega digito 3

int tabla(int x){ seg3 = num/256;

int resultado=0; seg3 = tabla(seg3);

const unsigned char binario[16]={ PORTD = (PORTD&0x00)|(seg3);

//a b c d e f g dp SegThree_ON;

0b00111111, //0 _delay_ms(30);

0b00000110, //1 SegThree_OFF;

0b01011011, //2

0b01001111, //3 //despliega digito 2

0b01100110, //4 seg2 = num/16;

0b01101101, //5 seg2&=0x0F;

0b01111101, //6 seg2 = tabla(seg2);

0b00000111, //7 PORTD = (PORTD&0x00)|(seg2);

0b01111111, //8 SegTwo_ON;

0b01100111, //9 _delay_ms(30);

0b01110111, //A SegTwo_OFF;

0b01111100, //B

0b00111001, //C //despliega digito 1

0b01011110, //D seg1 = num%16;

0b01111001, //E seg1 = tabla(seg1);


seg1 = tabla(seg1); }
PORTD = (PORTD&0x00)|(seg1); if(a == 0b00001000){
SegOne_ON; num-=1;
_delay_ms(30);

SegOne_OFF; if(num < 0x00){


}

num = 0x0FFF;
int main(void) }
{ }
DDRD=
DDRD|(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(
1<<6); //Configura PD7..PD0 como salida// do

PORTD &= 0x00; {

DDRB &= 0b00000111; MUX();

PORTB = PORTB|(1<<3)|(1<<4); } while ((PINB & 0b00011000) ==


0b00011000);

}
while (1)

if ((PINB & 0b00011000) != 0b00011000){

_delay_ms(45);

if((PINB & 0b00011000) != 0b00011000){

a = (PINB & 0b00011000); //Guardar valor pinc

do

MUX();

} while ((PINB & 0b00011000) != 0b00011000);

if(a == 0b00010000){

num+=1;

if (num >= 0xFFF)

num = 0;
Descripción de código

1. Se define una función "tabla" que mapea los valores binarios correspondientes a los
dígitos y letras hexadecimales en una matriz.

2. Se declaran variables globales para los segmentos de visualización (seg1, seg2, seg3),
una variable para el valor actual (num) y una variable auxiliar (a).

3. Se define una función "MUX" que actualiza los segmentos de visualización con los
dígitos correspondientes del número almacenado en "num".

4. En la función "main", se configuran los pines del microcontrolador y se establece un


bucle infinito.

5. Se verifica si se ha presionado uno de los dos botones en el puerto B. Si se detecta una


pulsación, se guarda el valor del puerto B en la variable "a" y se realiza una acción
dependiendo del valor de "a".

6. Si se presiona el botón correspondiente a incrementar el valor, se incrementa "num" en 1


y se verifica si ha alcanzado el límite máximo. En caso afirmativo, se reinicia a 0.

7. Si se presiona el botón correspondiente a decrementar el valor, se decrementa "num" en 1


y se verifica si ha alcanzado el límite mínimo. En caso afirmativo, se establece en el valor
máximo.

8. Después de realizar la acción, se llama a la función "MUX" para actualizar la


visualización en los segmentos de visualización.

9. Se repite el bucle principal, verificando constantemente si se ha presionado alguno de los


botones y actualizando la visualización en los segmentos de visualización según el valor
actual de "num".
Observaciones y Conclusiones

Gustavo Martinez: El uso de funciones, directivas #define y corrimientos facilita el desarrollo


del código, ya que permite una estructura modular y legible, promoviendo la reutilización de
código y simplificando las tareas de mantenimiento y modificaciones futuras.

Francisco Rosales: La eliminación de rebotes en las señales es fundamental para garantizar un


funcionamiento correcto y estable del sistema. Se debe investigar y aplicar métodos de software
para eliminar los rebotes y garantizar la precisión en la lectura del teclado y el control del
display.

Marco Reyes: El desarrollo de la práctica implica la combinación de conocimientos de


electrónica y programación, utilizando un microcontrolador y escribiendo código para manejar la
interfaz del teclado y el display de 7 segmentos.

Hibram Hermosillo: La creación de una función propia para la conversión al código de 7


segmentos nos permitió personalizar el proceso y adaptarlo a las necesidades específicas de la
práctica, en lugar de depender de librerías preexistentes.
Hoja de Verificacion
Referencias

López, J., & García, A. (2018). Design and Implementation of a 4x4 Keypad Interfacing with 7-
Segment Display using ATmega328 Microcontroller. International Journal of Engineering and
Technology, 7(3.27), 14-19.
Smith, R. (2016). Debouncing Techniques for Mechanical Keyboards.
Patel, S., & Sharma, M. (2020). Design and Implementation of Digital Counter using
ATmega328 Microcontroller with 7-Segment Display. International Journal of Advanced
Research in Electrical, Electronics and Instrumentation Engineering, 9(6), 6514-6518.

También podría gustarte