0% encontró este documento útil (0 votos)
74 vistas28 páginas

Arduinoplatformiovscode

Este documento proporciona una introducción a la arquitectura del microcontrolador ATMEGA328 utilizado en Arduino. Describe las unidades funcionales del ATMEGA328 como la memoria flash, SRAM, EEPROM, timers, ADC, USART, SPI e I/O digital. También describe placas Arduino como el Uno y Nano, y cubre funciones básicas de Arduino como pinMode(), digitalWrite(), digitalRead() y delay() para la configuración y el control de I/O digitales. Finalmente, explica cómo acceder directamente a los puertos del microcontrolador

Cargado por

Lucas Fiordelisi
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
74 vistas28 páginas

Arduinoplatformiovscode

Este documento proporciona una introducción a la arquitectura del microcontrolador ATMEGA328 utilizado en Arduino. Describe las unidades funcionales del ATMEGA328 como la memoria flash, SRAM, EEPROM, timers, ADC, USART, SPI e I/O digital. También describe placas Arduino como el Uno y Nano, y cubre funciones básicas de Arduino como pinMode(), digitalWrite(), digitalRead() y delay() para la configuración y el control de I/O digitales. Finalmente, explica cómo acceder directamente a los puertos del microcontrolador

Cargado por

Lucas Fiordelisi
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 28

Arquitectura del Microcontrolador ATMEGA328

y su programación utilizando Arduino

Autor: Prof. Ing. Carlos A. Robello


Para introducirnos en el mundo de Arduino manejado desde el punto de vista del hardware de los
microcontroladores estudiaremos al microcontrolador comercial, el ATMEGA328p por ser el que
utiliza el Arduino NANO. Dicho Arduino (en su versión Arduino Nano 3.x) tiene el MCU ATmega328P,
la misma que el Arduino Uno. La principal diferencia entre ellos es que el Arduino Uno tiene el
encapsulado PDIP (Plastic Dual-In-line Package) con 30 pines y la Arduino Nano está disponible en
TQFP (plastic quad flat pack) con 32 pines. Los 2 pines adicionales de la Arduino Nano sirven para
las funcionalidades del ADC, mientras que UNO tiene 6 puertos ADC pero la Nano tiene 8 puertos
ADC.
A continuación mostramos dicho encapsulado

página 1 de 28
página 2 de 28
A continuación vemos el diagrama en bloques del ATMEGA328P en le cual se muestra sus unidades
funcionales

Se ve que consta de:


32 K de memoria flash como ROM
2 Kbytes de memoria SRAM
1 Kbytes de memoria EEPROM para almacenar en formna no volatil datos temporale
2 Sistemas de Timer de 8 bits, (TIM0, TIM2)
1 Sistema de Timer de 16 bits, (TIM1
Los timers implemeta la funciones OUTPUT Compare, PWM e input capture,(solo el TIM1)
Lo que implementa 6 canales de PWM y ademas un contador de tiempor real
8 canales de conversión A/D de 10 bits, (solo en los encapsulados TQFP y QFN/MLF)
Insterfase USART
Interfase SPI maestro/esclavo
Comparador analógico
Sistema de reloj de perro guardian
Puertos de E/S y sus bits
23 bitsde E/S repartidos den tres puertos,(PORTB, PORTD de 8 bits y PORTC de 7 bits)

página 3 de 28
Tensión de trabajo:
de 2.7V a 5.5V for ATmega328P
Rango de temperatura:
de –40°C a +125°C

Velocidad del oscilador


0 a 8MHz de 2.7 a 5.5V (automotive temperature range: –40°C to +125°C)
0 a 16MHz de 4.5 a 5.5V (automotive temperature range: –40°C to +125°C)

Consumo de corriente:
Modo activo: 1.5mA para 3V a 4MHz
Modo Power-down 1μA a 3V

A continuación vemos un diagrama el bloque mas detallado

página 4 de 28
Ahora veámoslo desde elpunto de vista del Arduino

Placas Arduino
El arduino es una placa muy popular para diseño por lo que la misma tiene muchos modelos
Por ejemplo aquí discribiremos a los arduinos mas clasicos:

Arduino UNO:

Tal que sus características son

Microcontrolador: ATmega328
 Voltaje Operativo: 5v
 Voltaje de Entrada (Recomendado):7 – 12 v
 Pines de Entradas/Salidas Digital:14 (De las cuales 6 son salidas PWM)
 Pines de Entradas Análogas:6
 Memoria Flash:32 KB (ATmega328) de los cuales 0,5 KB es usado por Bootloader.
 SRAM:2 KB (ATmega328)
 EEPROM:1 KB (ATmega328)
 Velocidad del Reloj:16 MHZ.

Sabiendo que hay arduinos que tienen microcontrolador ATMEG328 con encapsulado TQFP y
QFN/MLF tiene 8 canales de conversión A/D y los que tiene encapsulado PDIP tienen 6 canales, sin
embargo los ARDUINOS UNO solo tiene 6 canales de conversión A/D tenga con cualquier tipo de
encapsulado para el microcontrolador.

página 5 de 28
A continuación veamos al Arduino UNO y la relación de las señale con los puertos del ATMEGA328

Arduino NANO:

página 6 de 28
A continuación observemos dos vistas del Pin Out, aquí se ve una de ellas

en la cual vemos que también están disponibles las entradas para canales de conversión A/D ADC6
y ADC7.

página 7 de 28
Aquí vemos otra vista del mismo

En ambos ARDUINOS UNO y NANO hay 14 entradas-salidas digitales


D0 y D1 se utiliza poco porque las mismas coinciden con RX y TX que son los pines de
comunicación serie RS232C para la programación del mismo,(teniendo en cuenta que para el caso
de los ARDUINO las mismas provienen de un conversor USB-SERIE.

Programación de ARDUINO
Hay varias formas de programarlos, mediante el IDE de ARDUINO o mediante editores

Nosotros vamos a usar la plataforma platformio bajo VSCODE.

Para usarlo debemos primero instalar


 IDE de ARDUINO, versión 1.6 o superior
 Luego entrar a VSCODE e instalar el plugin para ARDUINO
 Finalmente dentro de VSCODE instalar el platformio

Para instalarlo seguir los pasos indicados en le siguiente video

https://www.youtube.com/watch?v=5sn4L_y3ncA

página 8 de 28
primero vamos a utilizar las funciones nativas de Arduino, mas adelante pero muy pronto veremos el
manejo desde el punto de vista de los puertos de E/S y sus bits

Funciones básicas para manejo de ARDUINO

Configuración de los pines de E/S Digitales, la Función pinMode

sintaxis:

pinMode(pin,modo):

Configura el pin especificado por su número indicado en el parametro pin para comportarse como lo
indica el parámetro modo

modo puede valer:

 INPUT :configura el pin como entrada normal


 OUTPUT :configura el pin como salida
 INPUT_PULLUP :configura el pin como entrada a la que se le conecta internamente una
resistencia de PULL UP programada internamente

ejemplos:

para configurar el pin 2 de como entrada:

pinMode(2,INPUT);

para configurar el pin 4 de como entrada con resistencia de PULL_UP programada:

pinMode(4,INPUT_PULLUP);

para configurar el pin 8 como salida:

pinMode(8,OUTPUT);

Envío de datos hacia pines digitales configurados como salida,la función


digitalWrite:

sintaxís:

digitalWrite(pin, valor);

en la cual el parámetro valor puede asumir:

HIGH o 1 para envíar un 1 a la salida

página 9 de 28
LOW o 0 para envíar un 0 a la salida

Ejemplos:

para enviar un 1 hacia pin digital 6

digitalWrite(6,HIGH);
o
digitalWrite(6,1);

para enviar un 0 hacia pin digital 11

digitalWrite(11,0);
o
digitalWrite(11,LOW);

Recepción de datos desde pines digitales, la función digitalRead

sintaxis:

valor = digitalRead(pin);

En la cual valor es el valor lógico que entro por el pin configurado como entrada o el último valor
almacenado
en un pin configurado como salida.

Ejemplo:

para leer el estado de un sensor digital conectado al pin de entradas 3

sensor=digitalRead(3);

Para cambiar el estado del pin digital de salida 7 mostramos el siguiente tramo de código

vaux=digitaRead(7);
vaux=vaux^1; //transforma 0 en 1 0 1 en 0
digitalWrite(7,vaux);

Funciones para implementación de retardos no bloqueantes:

La función delay:

página 10 de 28
Detiene el programa un tiempo determinado expresado en milisegundos

sintaxis:

delay(nmilisec);

en la cual la variable nmilisec representada a la cantidad de tiempo expresada en milisegundos .


Como nmilisec es de tipo unsigned long se le puede poner lo que abarca el mismo.es decir 2 32 -1
como máximo valor en mseg

La función delayMicoseconds:
Detiene el programa un tiempo determinado expresado en microsegundos

sintaxis:

delayMicroseconds(nmicrosec);

en la misma la variable nmicrosec representada a la cantidad de tiempo expresada en


microsegundos. El parámetro nmicrosec es de tipo unsigned int pero el límite máximo de
micosegundos que se le puede poner es 16383

Ejemplo:

Se dispone de 1 pulsador pin digital 3. . También hay un de un led conectado en el pin digital 10.
El funcionamiento será el siguiente:
Al accionar el pulsador se debe apagar el LED y si se suelta se debe prender,(el led esta
inicialmente prendido

A continuación vemos el código


#include <Arduino.h>
#define PULS1 3
#define LED1 10

unsigned char dpuls1,dled1;

void setup() { pinMode(PULS1,INPUT_PULLUP);


pinMode(LED1,OUTPUT);
}

void loop()
{dpuls1=digitalRead(PULS1);
digitalWrite(LED1,dpuls1)
}

página 11 de 28
Como el pulsador está conectado en modo PULLUP el led estara prendido por hacer una
tranferencia directa al mismo

Como hacemos lo mismo pero manejandonos con los puertos del ATMEGA328

Puertos del ATMEGA328

Los registros DDRx sirven para programa el sentido de transferencia de los bits de los puertos, es
decir si son de entrada o de salida

Los registros PORTx sirven enviar datos hacia la de salida o leer su estadon configurados los bits
como salida

Los registros PINx sirven recibir datos desde la entrada estando configurados sus bits como entrada

(la x representada a la letra del puerto y quiere decia B, C o D según el puerto)


Según lo visto:
DDRB controla a los registros PORTB y PINB

DDRC controla a los registros PORTC y PINC

DDRD controla a los registros PORTD y PIND

 un 1 en un bit del registro DDRx configura el bit correspondiente del PORTx como salida

 un 0 en un bit del registro DDRx configura el bit correspondiente del PORTx como entrada
pero utiliza para la lectura al registro PINx

Vamos ahora a transformar el ejmplo a un que maneje los puertos

repitiendo alguno de los esquema de pines

página 12 de 28
Vemos que:
 el pin digital 3 esta ubicado en el bit 3 del PORTD
 el pin digital 10 esta ubicado en el bit 2 del PORTB

Primero configuremos el bits 3 del PORTD como entrada y el resto como salida y luego al bit 2 del
PORTB como salida y le resto como entrada, esto se hace para ver bien clara la diferencia

DDRD=0b11110111;
DDRB=0b00000100;

(0b delante del número significa que el número que sigue es binario)

En este microcontrolador para activar el modo PULLUP de los bits de entrado se requiere se envíe
un 1 al bit configurado como entrada

PORTD=0b00001000;

#include <Arduino.h>
unsigned char bitl;
void setup() {
DDRD=0b11110111;

página 13 de 28
DDRB=0b00000100;
PORTD=0b00001000;
}

void loop() {

bitl=(PIND & 0b00001000);


if(bitl==0)
PORTB=0b00000000;
else
PORTB=0b00000100;

Veamos que este 1° ejemplo es muy tosco por no tener en cuenta el estado de los otros bits de
ambos puertos

Antes de continuar veamos algunos concepto

Configuración de los puertos del ATMEGA328

Ya vimos que se usan los registros DDRB, DDRC y DDRC para los puertos respectivos que tienen la
misma letra final y además un 1 configura un bit corresponiente del puerto como salida y un 0 lo hace
como entrada

ejemplo 1)
En el puerto B se quieren configurar los bits 0 y 2 como entradas y el 7 y 5 como salidas
los otros bits no interesan:
Por lo dicho anteriormente hay que usar el registro DDRB

Un gráfico del como quedan los bits del regsitro DDRB se muestra a continuación

7 6 5 4 3 2 1 0
DDRB 1 x 1 x x 0 x 0

En los cuales la x significa estado indiferente, pero como algo hay que ponerle si le ponemos 0
el DDRB nos quedará así:

7 6 5 4 3 2 1 0
DDRB 1 0 1 0 0 0 0 0

En el cual resaltamos los bits a configurar


La sentencia a ejecutar sería las siguiente:

DDRB=0b10100000;

página 14 de 28
Pero la anterior forma es tosca pues no tiene en cuenta un posible estado anterior de los otros bits
pues los configura forzosamente como entradas.
Veamos ahora un método que deja el estado de los otros bits como estaban,
es el método del enmascaramiento

Para configurar los bits de salida efectuamos esta sentencia

DDRB=(DDRB | 0b10100000);

en la cual el operador | efectúa la operación Or lógica entre los bits del DDR y dato binario
10100000 que tiene en 1 a los bits correspondientes a configurar como salida en el DDRB y el resto
e 0 y como resultado final los bits 7 y 5 se pondrán en 1 independientemente de su estado pues or
entre 1 y cualquier otro estado da simpre 1.En cambio en los otros bits el or entre estos y 0 dará 0 si
valia 0 a 1 si valía 1 por lo tanto los deja inalterados. Para mas claridad veamos un ejemplo numérico
Al dato que sirve para establecer los estados lo denominarémos máscara1

7 6 5 4 3 2 1 0

DDRB 0 1 1 1 0 1 1 0
mascara1 1 0 1 0 0 0 0 0
DDRB | mascara1 1 1 1 1 0 1 1 0

Y se muestra en rojo que los bits 7 y 5 están en 1 independientemete del resto de los bits

Ahora vamos a configurar a los bits que tienen que ser configurados como entradas,

DDRB=(DDRB & 0b11111010);

vemos ahora que la máscara tiene 0 el la posición de los bits a configurar como entradas y 1 en los
restantes y además la operación es la AND lógica:& y como resultado final los bits 2 y 0 se pondrán
en 0 independientemente de su estado pues AND entre 0 y cualquier otro estado da siempre 0.En
cambio en los otros bits el and entre estos y 1 dará 0 si valia 0 o 1 si valía 1 por lo tanto los deja
inalterados: Si aplicamos estos al resultado del ejemplo anterior siendo que ahora la máscara se
denominará mascara0

7 6 5 4 3 2 1 0

DDRB 1 1 1 1 0 1 1 0
mascara0 1 1 1 1 1 0 1 0
DDRB & mascara0 1 1 1 1 0 0 1 0

página 15 de 28
Viendo de nuevo que los bits que estásn en rojo son los que cambiamos dejando inalteraros al resto
de los bits

Configuración de la resistencia de PULLUP

Veamos en detalle de que se trata

En dicho modo si el pulsador SW1 se acciona se produce un circuito cerrado que envía a
tierra al pin del microcontrolador,(0 volts), por lo tanto este recibe el estado lógico 0.
Si el pulsador se levanta el pin recibe practicamente 5 v atravez de la resistencia R1,(esto
es así porque la corriente que circula por la misma es muy baja y no genera caída de
tensión sobre ella, por lo tanto el pin recibe el estado lógico 1

Este y otros microntroladores tienen la posiblidad de programar las resistencias de PULLUP la que
de denomina ressistencia de PULLUP programada, y la hacelo evitamos poener una resistencia
externa. Si se quiere activar las resistencia de PULLUP en un bit configurado como entradad se debe
escribir un 1 al bit del puerto corresponiente,(no del DDRX)
Para nuestro ejemplo anterior si queremos que los dos bits de entrada tenga resistencia de PULLUP
programada

PORTB=(PORTB | 0b00000101);

en la cual se usa la mísca técnica de enmascaramiento para poner a “1”que empleamos en el


registro DDRB

página 16 de 28
Lectura de un bit de entrada

En este caso debemos usar la técnica de enmascaramiento no para cambair el estado de un bit sino
para determinar el estado del mismo, para ello el microcontrolador ATMEGA328 usa los regsitros
PINX en ekl cual la X es la letra del puerto corresponiente es decir PINB para el PORTB, PINC para
el PORTC y PIND para el registro PORTD
Por ejemplo determinemos el estado del bit 2 del ejemplo anterior

unsigned char bitl;


...
bitl=PINB & 0b00000100;

orden en la cual si el bit 2 vale 0 el AND dara 0 y este se asignará a la variable bitl, pero se vale 1
se asignará un valor distinto a 0 en la variable bitl, por eso le procedimiento se completa con un if

unsigned char bitl;


...
bitl=PINB & 0b00000100;

if(bitl != 0)
{

//accion a efectuar si el bit 2 valía 1


}
else
{
//accion a efectuar si el bit 2 valía 0

Entonces con este ejemplo podemos modifica el ejemplo anteriormente visto para que no modifique
ninguna configuración anterior

página 17 de 28
#include <Arduino.h>
unsigned char bitl;
void setup() {
DDRD=(DDRD & 0b11110111);//configura solo el bit 3 del PORTD como entrada
DDRB=(DDRB | 0b00000100);//configura solo el bit 2 del PORTB como salida
PORTD=(PORTD | 0b00001000);//activa la R de PULLUP interna del bit 3 del PORTD
}

void loop() {

bitl=(PIND & 0b00001000);


if(bitl==0) //si hay un 0 en la variable bitl
PORTB=(PORTB & 0b11111011);//pone a 0 solo el bit 3 del PORTB
else //sino
PORTB=(PORTB | 0b00000100);//pone a 0 solo el bit 3 del PORTB

Ejercicios de repaso

1. Configurar el bit 5 del puerto C como salida y el bits 4 del PORTD como entrada
2. Configurar los bits 3 y 4 del puerto D como salidas y los bits 0 y 5 del puerto B como entradas
3. Modificar el ejemplo anterior para que el bit de entrada que tiene conectado el pulsador sea el
bit 0 del PORTB y el de salida que tiene conectado el ledesea el 2 del PORTD y que además
el led se inice apagado y al accionar y mantener accionado el pulsador se encienda

Escribir un 1 o un 0 en un bit individuales de un puerto

Como vimos anteriormente si queremos por ejemplo escribir un 1 en el bit 3 del PORTB sin cambiar
los otros bits

PORTB=(PORTB | 0b00001000);

pues la operación OR en este caso no configura, sino escribe un 1 en el bit 3 del PORTB,pues la
operación OR con 1 da siempre un 1 y la operación OR con 0 deja inalterados los otros bits.

Si ahora lo queremos poner el mismo bit en 0

PORTB=(PORTB & 0b11110111);

o una forma mas elegante

PORTB=(PORTB & (~0b00001000));

página 18 de 28
en la cual el operador ~ invierte cada bit de las mascara con 1 en el bit a poner a 1 y la transforma
en la máscara de la sentencia que está mas arriba. Esto último sera muy util en el tema que viene a
continuación

Lectura del estado de los bits individuales de un puerto


Se utiliza tambíen la misma técnica de enmascaramiento pero utilizando los registros PINx cuando
los correspondientes bits de los puertos están configurados como entradas
Por ejemplo si queremos determinar el estado del bit 4 del PORTB que esta configurado com entrada
las sentencias serían las siguientes
unsigned char bl4;
bl4=(PINB & 0b00010000);

if(bl4 !=0)
{
//hacer algo para el caso en que e bit 4 es 1
}
else
{
// hacer algo para el caso en que e bit 4 es 0
}

Al efectuar la operación AND lógica entre el registro PINB y un dato que tenga todos los bits en 0
menos el que quiero inspeccionar en 1, si dicha operación da 0 quier decir que el bit vale 0, pues el
and bits entre el 1 de la posición 4 y el bit 4 da 0 si ese bit vale 0. En cambio si el and lógico da
distinto a 0 quiere decir que dicho bit vale 1. Si llamamos mascara4 al dato 00010000

7 6 5 4 3 2 1 0

PINB 1 0 0 1 0 1 1 1
mascara4 0 0 0 1 0 0 0 0
PINB & mascara4 0 0 0 1 0 0 0 0

Y evidentemente se ve que que el resutado de la operación AND de distinta a 0

Uso de macroórdenes para crear seudofunciones


para manejo de bits individuales de un puerto
Si tenemos un programa que prende , apaga o controla el estado varios bits en programa se puede
volver engorroso
Supongamos que se quiere generar la secuencia de un motor paso a paso bipolar para modo de

página 19 de 28
mayor fuerza, la misma tendría la siguiente tabla

pasos F4 F3 F2 F1
1 0 0 1 1
2 0 1 1 0
3 1 1 0 0
4 1 0 0 1
5 0 0 1 1

Para que la situación sea mas complicada. Asumamos que los controloes de las fases no están en
bits concecutivos ni en un mismo puerto es decir

F1 esta en: bit 0 del PORTB


F2 esta en: bit 1 del PORTC
F3 esta en: bit 3 del PORTD
F4 esta en: bit 5 del PORTB

Como se vería nuestro programa

#include <Arduino.h>

void setup() {
DDRB=(DDRB | 0b00000001);
DDRC=(DDRC | 0b00000010);
DDRD=(DDRD | 0b00001000);
DDRB=(DDRB | 0b00100000);
//ahora ponemos a 0 cada bit

PORTB=(PORTB & (~0b00000001));


PORTC=(PORTC & (~0b00000010));
PORTD=(PORTD & (~0b00001000));
PORTB=(PORTB & (~0b00100000));
}

void loop() {

PORTB=(PORTB | 0b00000001);
PORTC=(PORTC | 0b00000010);
PORTD=(PORTD & (~0b00001000));
PORTB=(PORTB & (~0b00100000));

delay(1000);

PORTB=(PORTB & (~0b00000001));


PORTD=(PORTD | 0b00001000);

página 20 de 28
delay(1000);

PORTC=(PORTC & (~0b00000010));


PORTB=(PORTB | 0b00100000);

delay(1000);
PORTB=(PORTB | 0b00000001);
PORTD=(PORTD & (~0b00001000));
delay(1000);
}

Ejemplo en el cual se ve que si bien fijamos el estado inicial de todas la fases y luego cambiamos
solo las que pasan de 1 a 0 o viceverza el programa es engorroso e inmanejable

Entonces implementemos este cambio

Primero veamos una forma elejante de armar las mascaras para un solo bit

por ejemplo para crear la máscara que controla al bit 5 del PORTB se puede poner

1<<5

pues el operador << aplicado a un número equivale a desplazamiento a izquierada tantos bits como
indica en número, es decir

00000001 << 5 da como resultado 00100000

y ademas dicho 5 puede estar en una variable

y por lo tanto la sentencia completa se escribría así

PORTB=(PORTB | (1<<5));

Si el bit se representa con la variable n

PORTB=(PORTB | (1<<n));

Ahora utilizando la macroorden #define podemos implementar esta sudofunción

#define prenderbit(puerto,nbit) puerto=(puerto | (1<<nbit))

y emplearla de esta forma

prenderbit(PORTB,5);

página 21 de 28
Del mismo modo la macroorden para pone a 0 se escribiría así

#define apagarbit(puerto,nbit) puerto=(puerto &( ~(1<<nbit)))

y como ejemplo ponemos ser el caso de

PORTD=(PORTD & (~0b00001000));

que se transforma previamente en

PORTD=(PORTD & (~(1<<3)));

que ahora se escribiría así

apagarbit(PORTD,3);

Entonces el programa del motor paso a paso nos quedaría así:

#include <Arduino.h>
#define prenderbit(puerto,nbit) puerto=((puerto | (1<<nbit)))
#define apagarbit(puerto,nbit) puerto=(puerto &(~(1<<nbit)))

void setup() {

prenderbit(DDRB,0);
prenderbit(DDRC,1);
prenderbit(DDRD,3);
prenderbit(DDRB,5);
//ahora ponemos a 0 cada bit
apagarbit(PORTB,0);
apagarbit(PORTC,1);
apagarbit(PORTD,3);
apagarbit(PORTB,5);
}

void loop() {
prenderbit(PORTB,0);
prenderbit(PORTC,1);
apagarbit(PORTD,3);
apagarbit(PORTB,5);

delay(1000);

apagarbit(PORTB,0);
prenderbit(PORTD,3);

delay(1000);

página 22 de 28
apagarbit(PORTC,1);
prenderbit(PORTB,5);

delay(1000);

prenderbit(PORTB,0);
apagarbit(PORTD,3);

delay(1000);
}

Macrodefinición para leer el estado de un bit de entrada:

Par el caso de la expresión para leer en estado del bit 4 del PORTB

bl4=(PINB & 0b00010000);

la expresión que usa máscara obtenida de desplazamientos nos quedaría así

bl4=(PINB & (1<<4));

La macrodefinición completa se puede implementar de la siguiente manera:

#define leerbit(puerto, nbit) (puerto & (1<<nbit))

Solo que en este caso sei el bit vale 0 pregunto por 0 pero si vale 1 pregunto por distinto a 0

Si queremos mas claridad a la hora de hacer el programa tal que si el bit vale 1 preguntemos por 1 y
se vale 0 preguntemos por 0 podemos utilizar la sentencia
if inline que tiene la siguiente forma:

resultado= Pregunta ? exprev : expref

en la cual si la pregunta es verdadera se resuelve exprev y se asigna a la variable resultado, por otro
lado si la pregunta es falsa se resuelve expref y se asigna a la variable resultado
por ejemplo:

x = 10;
y = x > 9 ? 100 : 200;

en este caso se asigna 100 a la variable y


Para el caso de la macrodefinicíon lo mas claro al utilizarla es definirla de la siguiente forma

página 23 de 28
#define leerbit(puerto,nbit) (((puerto & (1<<nbit)) != 0) ? 1 : 0)

(NOTA:los paréntesis que engloban a toda la expresión se deben poner para que la
macro expasión funciones correctamente por el problema de la precendencia)

en la cual la expresión sera el resultado de la macrodifiniciíon de manera tal que si el


enmascaramiento da distinto a 0 por ser 1 el bits a inspeccionar se genera un 1 y en caso contrario
se genera un 0

Veamos las 3 macros en un ejemplo anterior que hacia que un led siga esl estado de un pulsador
El cambio que hacemos es que el led está conectado en el bit 0 del PORTB,(pin digital 8) y el
pulsador está conectado en el bit 2 del PORDT,(pin digital 2) y tiene activada su resistencia de
PULLUP

#include <Arduino.h>

#define prenderbit(puerto,nbit) puerto=((puerto | (1<<nbit)))


#define apagarbit(puerto,nbit) puerto=(puerto &(~(1<<nbit)))
#define leerbit(puerto,nbit) (((puerto & (1<<nbit)) != 0) ? 1 : 0)

unsigned char bitl;


void setup() {

apagarbit(DDRD,2);//configura solo el bit 2 del PORTD como entrada


prenderbit(DDRB,0);//configura solo el bit 0 del PORTB como salida
prenderbit(PORTD,2);//activa la R de PULLUP interna del bit 2 del PORTD

void loop() {

if(leerbit(PIND,2)==1)
prenderbit(PORTB,0);//pone a 1 solo el bit 0 del PORTB
else //sino
apagarbit(PORTB,0);//pone a 0 solo el bit 0 del PORTB
}

finalmente para mas practicidad nos conviene copiar las 3 macros en un archivo de cabacera y
luego la unimos en cada proyecto y la incluimos la podemos llamar manejodebits.h
y la misma contiene las tres macros,(el archivo los podemos crear desde el editor del VSCODE
con el menú file-new y luego lo tenemos que salvar en el directorio include del proyecto con dicho
nombre. Despues le compiamos las macros lo salvamos, incluimos el archivo de cabacera y el
proyecto nos queda ahora así:

página 24 de 28
#include <Arduino.h>
#include "manejodebits.h"

unsigned char bitl;


void setup() {
apagarbit(DDRD,2);//configura solo el bit 2 del PORTD como entrada
prenderbit(DDRB,0);//configura solo el bit 0 del PORTB como salida
prenderbit(PORTD,2);//activa la R de PULLUP interna del bit 2 del PORTD
}

void loop() {

if(leerbit(PIND,2)==1)
prenderbit(PORTB,0);//pone a 1 solo el bit 0 del PORTB
else //sino
apagarbit(PORTB,0);//pone a 0 solo el bit 0 del PORTB
}

para otros proyectos:


 podemos copiar desde windows el archivo de cabacera
manejodebits.h al directorio include de otro o un nuevo proyecto
 Podemos seleccionar el archivo en el proyecto actual y salvarlo en el directorio include de otro
proyecto

Macroexpasión para invertir el estado de un bit:

Si bien se puede invertir el estado de un bit utilizando las macoexpansiones vista es recomendable
tener una macroexpasión para invertir el estado de un bit, pues el operador lógico en lenguaje C
existe y se puede usar para tal motivo. Se trata del operador lógico bit a bit para implementar la
operación lógica OR Exclusiva o XOR, que se implementa con el caracter ^,(si es dificil tipearlo con
teclado se puede obtener tipeando ALT+94 del teclado keypad)

Poe ejemplo si queremos invertir el estado del bit 4 del PORTd escribimos

PORTD=(PORTD ^ 0b00010000);

expresión en la cual solo se invierte del PORTD los bit que se corresponden a los que en la máscara
valen 1 es decir solo el bit 4 para nuestro caso, pues la operación XOR entre un bit y 0 no cambia el
bit y la operación XOR con 1 si lo invierte

Si escribimos la macro la podemos definir así:

#define invertirbit(puerto,nbit) puerto=(puerto ^ (1<<nbit))

página 25 de 28
Entonces las macros sería ahora las siguientes

#define prenderbit(puerto,nbit) puerto=((puerto | (1<<nbit)))


#define apagarbit(puerto,nbit) puerto=(puerto &(~(1<<nbit)))
#define invertirbit(puerto,nbit) puerto=(puerto ^ (1<<nbit))
#define leerbit(puerto,nbit) (((puerto & (1<<nbit)) != 0) ? 1 : 0)

y también esta se agregaría al archiovo de cabecera mandejodebits.h

Macroexpasión en otros idiomas:


También se puede utilizar otros idiomas diferentes al ingles o español.
Por ejemplo en idioma Catalan nuestras macros se verían así:

#define callarbit(puerto,nbit) puerto=((puerto | (1<<nbit)))


#define apagarbit(puerto,nbit) puerto=(puerto &(~(1<<nbit)))
#define invertirbit(puerto,nbit) puerto=(puerto ^ (1<<nbit))
#define llegirbit(puerto,nbit) (((puerto & (1<<nbit)) != 0) ? 1 : 0)

Traten de elegir un idioma que sea claro para Uds. en la programación

Configuración del sentido de Puertos completos

Por ejemplo si queremos configurar como salidas todos los bits del PORTC

DDRC=0b01111111;

(los bits son y 7 porque este puerto en el ATMEGA328 tiene disponibles los bits que van del 0 al 6, el
7 no existe)

Para configurar ahora como salidas los bits del PORTB que van desde en 0 a 5 sin tocar las otras,
(Pues en 6 y 7 estan las líneas que corresponden al oscilador a cristal XTAL1 y XTAL2
respectivamente)

DDRB=(DDRB | 0b00111111);

de esta forma la máscara deja inalterados a los bits 0 y 1)

Si ahora queremos configurar todos los bits indicados como entradas

DDRB=(DDRB & 0b11000000);

Por último si quermos configurar los bits 0 a 3 como entradas y 4, 5 como salidas recomiendo esta
forma porque son menos sentencias

página 26 de 28
Primero configuro los 4 primeros bits como entrada sin tocar los otros bits

DDRB=(DDRB & 0b11110000);

y ahora configuro los bits 4 y 5 como salidas sin tocar los otros

DDRB=(DDRB | 0b00110000);

Igual este es un ejemplo de como configurar bits de un puerto sin tocar otros,(6 y 7 del PORTB en
este caso) pues las unidades funcionales como las del cristal tiene una configuración que es
prioritaria por sobre la que se logra con los DDRx y las seencias anteriores se pueden resumeir en
esta

DDRB= 0b00110000;

o esta

DDRB= 0b11110000;

o cualquier combinación que puedan tener los bits 6 y 7

DDRB= 0bxx110000;

Pero ahora veamos un caso en que si es importante mantener el estado de los bits que no tenemos
en cuenta

Conigurar los bits 1 a 3 del PORTB como salidas, el bit 5 como entrada y no tocar a los bits 0 y 4
(bits 6 y 7 son indiferentes)

Primero configuramos a los que son de salida

DDRB=(DDRB | 0b11001110);

y ahora el que es de entrada

DDRB=(DDRB & 0b11011111);

Envío de datos completos a los de Puertos

Se utiliza la misma técnica pero utilizando los registros PORTx

Por ejemplo para enviar el dato binario 1010011 al PORTC qu está totalmente configurado como
salida se ejecuta

PORTC=0b01010011;

página 27 de 28
Si el puerto esta configurado como entrada también se le pueden enviár datos pero esto equivale a
habilitar o desabilitar la resistencia de PULLUP interna

Tomemos de nuevo el PORTC y configuremos a los bits 0,1,2 y 6 como entradas y a los bits 3,4 y 5
como salidas

DDRC=0b00111000;

y ahora activemos las resistencias de PULLUP internas de los bits 0,2 y 6 y desactivemos
del del bit 1

PORTC=0b01000101;

Lectura de datos completos desde un puerto

Si ahora se necesitan leer varios bits que está ubicados en un mismo puerto también lo podemos
hace con una sola orden como lo hacíamos con un puerto de salida.
Por ejemplo:

Si queremos leer el dato de 8 bits que proviene del PORTB en forma completa y asignarlo a una
variable,(recordando que es estado de los bits 6 y 7 no tiene sentido

unsigned char A;
...
DDRB=0b00000000;

A=PINB;
Si queremos enviar el dato de 8 bits que proviene del puerto D,(configurado como entrada hacia el
puerto B,(configurado como salida)

PORTB=PIND;

página 28 de 28

También podría gustarte