Primeros Pasos Pic 2018
Primeros Pasos Pic 2018
Primeros Pasos Pic 2018
MICROCONTROLADORES PIC®
Y MPLAB X.
1
Coria, Ariel Jacinto
Primeros pasos con microcontroladores PIC® Y MPLAB X / Ariel Jacinto Coria;
editado por Alejandro Anibal Airoldi. - 1a ed . - Ciudad Autónoma de Buenos Aires
mcelectronics, 2018.
192 p. ; 23 x 16 cm.
ISBN 978-987-3702-09-9
© mcelectronics
Diseño de Portada:
54Designers.com - Comunicación + Innovación
2
A nuestras familias y amigos por el apoyo incondicional.
3
4
SUMARIO
MPLAB X IDE Y HERRAMIENTAS DE DESARROLLO 7
Instalando las herramientas necesarias para programar.
MPLAB X, XC8 y Code Configurator
PRIMERAS PRÁCTICAS 40
Se verán los pasos necesarios para crear
nuestra primera aplicación completa. Lectura de pulsadores,
encendido, parpadeo y rotación de LEDs.
CONVERSOR ADC 67
Aprenda a utilizar el conversor analógino digital para leer
diferentes sensores conectados al PIC. Múltiples canales
de entrada.
USART 107
Aprenda a realizar comunicaciones de datos. Transmisión y
recepción por USART.
5
PULSO CARDÍACO 147
Veremos cómo configurar y utilizar un sensor de pulso cardíaco.
Consideraciones importantes para lograr una buena medición.
6
MPLAB X IDE Y HERRAMIENTAS DE DESARROLLO
INTRODUCCIÓN
Este libro pretende ser una ayuda auxiliar al curso de programación de
microcontroladores PIC en lenguaje C. Durante el curso podrá aprender a
programar los microcontroladores de manera eficiente y rápida gracias a las
herramientas de desarrollo provistas por la empresa Microchip. En una
primera etapa, aprenderemos cuales son las herramientas que disponemos
para desarrollar nuestros proyectos, cual será nuestro entorno de trabajo
diario con estos microcontroladores y además nos adentraremos a un nivel de
programación más poderoso que el lenguaje Assembler, realizando nuestros
proyectos en lenguaje C.
La presente edición del libro cuenta además con prácticas especiales utilizando
las placas Click de MikroElectronica, donde aprenderemos a manejar sensores
de temperatura-humedad-presión y acelerómetros y giroscopios.
HERRAMIENTAS DE DESARROLLO
Para trabajar en el desarrollo de código para microcontroladores, debemos
primero familiarizarnos con las herramientas de desarrollo que nos provee, en
este caso, la empresa Microchip para la utilización de sus microcontroladores
PIC. La filosofía de Microchip es la de juntar todas las herramientas en un solo
entorno de desarrollo llamado MPLAB X IDE (Integrated Development
7
Enviroment). Este entorno de desarrollo posee todo lo necesario para trabajar
con los microcontroladores PIC. Veamos cuáles son sus características.
MPLAB X IDE
El MPLAB X IDE es el entorno de trabajo que nos provee de todas las
herramientas necesarias para el completo desarrollo de un proyecto con
microcontroladores PIC. Además abarca todas las familias de
microcontroladores PIC, desde la línea base de 8 bits, hasta los más avanzados
PIC de 32bit.
8
COMPILADOR XC8
Debido a la creciente complejidad en los desarrollos de sistemas embebidos, el
aumento de los recursos de los procesadores y que a su vez requieren de un
tiempo cada vez más acotado para el desarrollo de un proyecto para que este
salga al mercado, hicieron que se buscará una alternativa a las dificultades que
plantea el lenguaje assembler. Uno de los lenguajes de alto nivel más
populares y de gran potencialidad es el lenguaje C, ya que posee características
que lo hacen ideales en sistemas microcontrolados. A pesar de que C es un
lenguaje de alto nivel, también nos ofrece la posibilidad de acceder a
posiciones de memoria y posee un gran manejo de bits, lo que lo hace un
lenguaje ideal en los sistemas embebidos.
9
Estas son las características del compilador XC8:
10
Entre los recursos básicos podemos ver que la placa Xpress cuenta con su
propio programador por USB, un potenciómetro, un pulsador, LEDs y un sensor
de temperatura.
11
PIC16F18855
Este microcontrolador de 8 bits, pertenece a la familia PIC16F18XX, los cuales
son microcontroladores diseñados para aplicaciones de propósitos generales y
poniendo hincapié en el bajo consumo. A continuación enunciaremos las
características más importantes de esta familia de PIC:
12
Nuestro microcontrolador, con el que trabajaremos en nuestras prácticas, es el
PIC16F18855, el cual posee 14KB de memoria de programa, 1KB de memoria
RAM, 256Bytes de memoria EEPROM interna y un oscilador interno que puede
alcanzar hasta los 32 MHz.
13
PRACTICAS MPLAB X
ELEMENTOS NECESARIOS:
Para esta clase es necesario contar con los siguientes elementos:
-Descargar instalador del MPLAB X IDE de la página de Microchip
-Descargar el instalador del Compilador XC8 de la página de Microchip
-Descargar los archivos fuentes provistos en la clase
14
2) Dirigirse a la ubicación del archivo descargado y ejecutar el instalador,
que para un usuario de Windows debe tener un nombre como con el
siguiente formato
MPLABX-vX.XX-windows-installer.exe.
15
4) En la siguiente ventana seleccionar la opción “I accept agreement” y
oprimir Next.
16
6) La siguiente ventana nos pregunta que deseamos instalar, el MPLAB X
IDE, que es el entorno de desarrollo donde vamos a trabajar, o el
MPLAB IPE que es un entorno de programación. Seleccionar ambas
herramientas y Continuar.
17
7) Comenzar la instalación.
18
8) Una vez finalizada la instalación, la ventana final posee unos checkbox
los cuales debemos dejar habilitados si queremos que al finalizar el
buscador Web nos lleve a la página de descarga de los compiladores.
19
Luego de esto la instalación está completa solo resta ejecutar la
aplicación.
20
4) Aceptar la licencia y oprimir Next
21
5) Seleccionamos el tipo de licencia con el que utilizaremos, si queremos
utilizarlo de forma gratuita seleccionamos Free. Si posee licencia para
trabajar en el modo Workstation o Network Client, seleccione el
correspondiente.
22
6) Seleccionamos el directorio donde se instalará el compilador. En este
caso es preferible dejarlo en el directorio por defecto.
23
7) En la siguiente ventana debemos configurar algunas características del
compilador. El primer CheckBox indica si la configuración de esta
ventana se aplica a todos los usuarios. Los siguientes dos CheckBox
crean un path del compilador XC8 en las variables de entorno. Si se
desea utilizar el compilador desde una ventana de comandos, dejar
estos checkbox habilitados, en caso contrario no. Los últimos dos
Checbox se sugieren dejar deshabilitados si se desea mantener los
antiguos proyectos con los antiguos compiladores.
24
8) Ya estamos listo para iniciar la instalación, hacemos click en Next y
aguardamos a que finalice la instalación.
25
9) Luego de la instalación, si posee la clave de activación puede agregarlo,
en caso contrario puede utilizar el compilador de manera gratuita
(Free) o activar la versión de prueba durante 60 dias. Seleccione alguna
de estas o simplemente presión Next para continuar en modo Free.
26
10) Finalmente completamos la instalación oprimiendo en Finish en la
última ventana. De esta manera ya podemos ejecutar el MPLAB X IDE y
utilizar el compilador desde este entorno de desarrollo.
27
PRÁCTICA 3: INSTALACIÓN DEL MPLAB CODE CONFIGURATOR
En esta práctica se verán los pasos para la descarga y la instalación de la
herramienta de configuración MPLAB Code Configurator.
28
2) La acción abrirá una nueva ventana dedicado al manejo e instalación
de los Plugins en MPLAB X. Allí seleccionamos la ventana la solapa
Available Plugins para ver los plugins disponibles en nuestra versión de
MPLAB X. Seleccionamos la que dice MPLAB Code Configurator v3 y
hacemos click en “install”.
29
3) Una vez que se oprime el botón “Install” se ejecutara el instalador del
MPLAB Code Configurator. En la ventana de bienvenida hacemos click
en Next.
30
4) Luego aceptamos la licencia y hacemos click en Install para comenzar
la instalación.
31
5) Esperamos a que la instalación finalice, una vez que lo hace, la ventana
final nos indicará que el proceso de instalación finalizo exitosamente y
hacemos click en Finish.
32
6) Para iniciar la ejecución del MPLAB Code configurator debemos
hacerlo desde la interfaz del MPLAB X haciendo click en Tools >>
Open/Close MPLAB Code Configurator v3.
33
PRÁCTICA 4: CREAR UN NUEVO PROYECTO EN MPLAB X
En esta práctica se verán los pasos para crear un nuevo proyecto utilizando el
entorno de desarrollo del MPLAB X.
34
3) En la siguiente ventana debemos seleccionar el microcontrolador a
utilizar, en este caso seleccionamos el micro utilizado en la placa
Microchip Xpress Board que es el PIC16F18855.
35
4) En la próxima ventana, de manera opcional, podemos incluir el soporte
para agregar un Header que se utiliza para aumentar algunas
funcionalidades para el Debug del Proyecto. En nuestro caso no
utilizaremos ningún hardware adicional para esto, así que dejamos
esta opción en “None”.
36
5) En la siguiente ventana seleccionamos el programador con el cual
trabajaremos. No podemos seleccionar la placa Xpress, ya que este tiene un
modo diferente de cargar el firmware que otras placas de desarrollo. En este
caso lo que debemos hacer es seleccionar el PICKit3 o el ICD3, porque en
realidad lo que necesitamos es que se genere un archivo “.hex”.
37
6) En la siguiente ventana seleccionamos el compilador disponible para el
Microcontrolador de la placa Xpress. Seleccionamos el compilador
XC8.
38
7) En la última ventana de la creación del proyecto debemos agregar el
nombre del proyecto y elegir una carpeta en donde se crearan los
archivos. En este caso, a nuestro primer proyecto lo llamaremos
“hello_world”
39
PRÁCTICA 5: ENCENDIDO DE UN LED.
En esta práctica se verán los pasos necesarios para crear nuestra primera
aplicación completa en cual aprenderemos a encender el LED D4 la placa
Xpress.
40
2) Una vez que esta creado el proyecto, ejecutamos el MPLAB Code
Configurator haciendo click en Tools >> Embedded >> MPLAB Code
Configurator v3. Esto ejecutara el MCC para configurar los periféricos
del PIC.
41
4) Luego seleccionamos en la ventana Project Resources la sección Pin
Module. Esto abrirá la ventana central que se verá como muestra la
siguiente imagen, donde todavía no se selecciono ningún pin.
42
5) Para agregar pines a esta lista debemos dirigirnos a la ventana Pin
Manager y seleccionar el Pin del microcontrolador que actuara como
salida, manejando el encendido del LED. El LED D4 se encuentra
conectado al pin RA2, así que buscamos este pin en la fila Output y lo
seleccionamos haciendo click sobre el candado, para bloquear el
funcionamiento de este pin como solo salida. En la siguiente imagen
vemos como debería quedar esta ventana.
43
6) Una vez que seleccionamos el pin como salida, vamos a la ventana Pin
Module y modificamos el nombre IO_RA2 por el de D4. También
activamos el checkBox de Output, indicando que el pin se configura
como salida. Para que se encienda el Led al iniciar el programa
debemos habilitar la casilla de “Start High”.
44
7) Por último hacemos click en “Generate”, esto nos da la opción de
guardar la configuración en un archivo mc3. Guardamos y podemos
cerrar el MPLAB Code Configurator. En la ventana de proyectos
veremos que se generan nuevos archivos fuentes, entre ellos el
archivo “main.c” y las carpetas “MCC Generated Files” que contiene
los códigos fuentes para configurar el sistema y los pines.
45
8) Abrimos el archivo main.c generado por el MPLAB Code Configurator y
vemos que la función main tiene el formato visto en la clase, donde
nos encontramos con una función que realiza la configuración del
sistema SYSTEM_Initialize(); y luego el loop principal.
46
9) Finalmente procedemos a compilar el proyecto, hacemos click en el
botón Build, si todo está bien y no hay errores de sintaxis,
debemos ver el mensaje “BUILD SUCESSFUL”. Luego de esto podemos
programar el microcontrolador.
47
PRÁCTICA 6: BLINK LED.
En esta práctica se verán los pasos necesarios para crear una aplicación que
realice el encendido intermitente del LED.
48
3) Luego dirigirse a la ventana de Pin Manager y seleccionar los pines
RA0, RA1, RA2 y RA3 como salida.
49
4) En la ventana Pin Module, verificar que el pin RA2 se encuentre
marcado como salida y colocarle nombre “D4” para indicar que es este
LED de la placa Xpress el que se va a encender.
50
5) Una vez hecho esto, hacemos Click en el botón Generate, lo cual
creara en nuestro proyecto los archivos necesarios para la
configuración del sistema y la inicialización del PIN que controlara el
encendido del LED. También se creara el archivo main.c, igual que al
ejercicio anterior, hacemos doble Click sobre este archivo para abrirlo
y modificarlo.
51
el LED con la función D4_SetLow(); y se vuelve a esperar otros 500
milisegundos.
52
PRÁCTICA 7: ESTADO DEL PULSADOR A LA SALIDA
53
3) Luego de configurar las preferencias del sistema, vamos a configurar
los pines GPIO del microcontrolador. Para ello vamos a la ventana
Project Resources del MCC y seleccionamos Pin Module, esto abrirá en
la ventana central una tabla donde se listaran la configuración de los
pines. Para ir agregando filas a esta tabla debemos dirigirnos a la
ventana Pin Manager y vamos a configurar dos pines, uno como
entrada y otro como salida. El pin de entrada será RA5, el cual está
conectado al pulsador S2 y el pin RA2 como salida, donde se encuentra
el LED D4. La ventana Pin Manager se debe ver como lo muestra la
siguiente Figura.
54
4) Una vez seleccionados los pines, nos dirigimos a la ventana central del
MCC, donde deben estar listado los pines recién seleccionados, allí
modificamos el “Custom Name” de ambos pines, para que el de la
entrada diga S1, que es el nombre del pulsador en la placa y el de
salida el nombre D4, por el LED.
55
5) Luego de tener los pines de entrada y salida configurados, debemos
generar el código, para ello presionamos sobre el botón Generate,
para que se creen los fuentes del proyecto. Luego abrimos el archivo
main.c generado y agregamos la siguiente línea de código, como
muestra la figura. Esta línea de código realiza la asignación de la salida
que controla el LED con el valor leído del pulsador. La asignación se
hace negada por cómo está conectado el pulsador, ya que en estado
abierto la tensión leída en el pin es 5V y cuando se oprime se va a cero.
56
6) Finalmente procedemos a compilar el proyecto, hacemos click en el
botón Build, si todo está bien y no hay errores de sintaxis,
debemos ver el mensaje “BUILD SUCESSFUL”. Luego de esto podemos
programar el microcontrolador y verificamos el funcionamiento del
mismo, el cual debe encender el LED D4 siempre que se oprima el
pulsador.
57
PRÁCTICA 8: ROTACIÓN DE LEDS
En esta práctica se verán los pasos para crear un proyecto en donde
realizaremos el encendido secuencial de los LEDs de la placa Curiosity.
58
3) Luego Seleccionamos en la ventana de Project Resources el apartado
del Pin Module y nos dirigimos a la ventana de Pin Manager para
seleccionar como salida digital los cuatro pines que controlan el
encendido del LED, que son los Siguientes:
RA0 – LED D2
RA1 – LED D3
RA2 – LED D4
RA3 – LED D5
59
4) Luego en la ventana central, debemos agregar los Custom Name de
cada PIN, los cuales tendrán el nombre de cada LED que se encuentra
en la placa.
60
proyecto. Abrimos el archivo main.c y agregamos el siguiente código,
para que se vea como el código siguiente.
#include "mcc_generated_files/mcc.h"
// prototype Delay Function
void Delay(void);
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
unsigned char rotateReg;
// Initialize temporary register to begin at 1
rotateReg = 1;
// Begin with D4 High
D2_LAT = 1;
// Clear LEDs
D3_LAT = D4_LAT = D5_LAT = 0;
while (1)
{
// Add your application code
// Call the Delay() function and keep LED on for 0.5 secs
Delay();
// Shift value in temp register to the right by 1 bit
rotateReg <<= 1;
// If the last LED has been lit, restart the pattern
if(rotateReg == 16)
rotateReg = 1;
// Determine which LED will light up
// ie. which bit in the register the 1 has rotated to.
D2_LAT = rotateReg & 1;
D3_LAT = (rotateReg & 2) >> 1;
D4_LAT = (rotateReg & 4) >> 2;
D5_LAT = (rotateReg & 8) >> 3;
}
}
// Delay function to keep the LED on for 0.5 secs before rotating
void Delay(void)
{
int i = 0;
for(i=0;i<25;i++){
__delay_ms(20);
}
61
}
Descripción:
El pulsador o switch es un elemento muy utilizado en la generación de
interfaces con el usuario, de esta manera se puede acceder datos al sistema
utilizando este elemento. Pero un botón mecánico tiene la desventaja de que
el cambio de estado no se produce de manera suave, sino que se producen
62
cambios erráticos hasta que se toma el estado deseado. Este comportamiento
puede generar que se detecten más de un cambio de estado cuando en
realidad solo se oprimió una vez el pulsador. Para evitar esto, normalmente lo
que se hace es detectar el primer cambio de estado y generar un retardo que
enmascare por un tiempo los siguientes valores hasta que se alcance la
estabilidad del accionamiento mecánico y evitar detectar otras posibles
activaciones “espurias” del pulsador. Otro modo de atacar este problema es el
de tomar muestras equi-espaciadas del estado del pulsador y determinar si se
produce la activación o no del mismo. Este segundo método es el que se
realizara en la práctica, se tomaran muestras del pulsador cada 50
milisegundos para detectar o no, los cambios de estados.
Periféricos utilizados:
Para esta práctica se utilizaran los pines GPIO para activar el encendido de los
LEDs de la placa y leer el estado del pulsador.
63
Código:
#include "mcc_generated_files/mcc.h"
/*
Defines
*/
#define BUTTON_PRESSED 0
#define BUTTON_NOT_PRESSED 1
/*
Prototypes
*/
bool BUTTON_IsPressed(void);
bool BUTTON_Activate(void);
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
while (1)
{
// Add your application code
if(BUTTON_Activate())
{
D2_Toggle();
}
64
__delay_ms(50);
}
}
Funciones Auxiliares
//Funciones Auxiliares
bool BUTTON_IsPressed(void)
{
return ( (BUTTON_PORT == BUTTON_PRESSED) ? true : false);
}
bool BUTTON_Activate(void)
{
static bool lastState = false;
bool actualState;
bool activateButton;
actualState = BUTTON_IsPressed();
if( actualState && !lastState)
activateButton = true;
else
activateButton = false;
lastState = actualState;
return activateButton;
}
65
tiempo, ya que esta toma una muestra del estado anterior y la compara con el
actual, si hubo un cambio, en este caso pasa de no oprimido a oprimido, la
función devuelve True. Por ello es importante que el tiempo de muestreo sea
lo suficientemente grande como para evitar el efecto rebote.
66
PRÁCTICAS CON PERIFERICOS DEL PIC
En esta práctica se verán los pasos para crear un proyecto en donde realizara la
configuración del modulo ADC del microcontrolador para obtener el valor de la
velocidad de rotación de los LEDs de la placa utilizando como control la
posición del potenciómetro.
67
Ventana Pin Module
68
modo que se hizo en la práctica anterior. En la siguiente imagen vemos
la configuración que debe quedar en el modulo ADC
4) Una vez configurado los pines GPIO y el conversor ADC, generamos los
códigos fuentes haciendo click en el botón Generate. Luego cerrar el
MCC, dirigirse a la ventana de proyecto y abrir el archivo main.c,
agregar el código que se muestra a continuación.
69
#include "mcc_generated_files/mcc.h"
/*
Main application
*/
void main(void) {
// initialize the device
SYSTEM_Initialize();
// Clear LEDs
D2_LAT = D3_LAT = D4_LAT = D5_LAT = 0;
while (1)
{
// Add your application code
// Set DELAY from the top 8 MSbs of ADC
delay = ADCC_GetSingleConversion(POT1) >> 8;
// Decrement the 8 MSbs of the ADC and delay 2ms for each
while (delay-- != 0)
__delay_ms(2);
70
}
Descripción:
Gracias a la ayuda del nuevo modulo computacional, realizar la tarea
propuesta como ejercicio resulta fácil de generar y con simplemente una
cuantas líneas de código que configuran el modulo ADC²(Esto se realiza desde
el MCC). El enunciado pide dos cosas, una es la de tomar valores medios de
una cantidad de muestras y por otro lado la de detectar si el resultado de la
conversión no supera determinados umbrales. La primera parte, obtener el
valor medio, se puede generar configurando al modulo computacional en el
modo Burst Average, esto hace que la sección del Acumulador, sume sucesivas
muestras del conversor y luego realice un desplazamiento del registro para
generar la división y obtener el valor medio. La segunda parte, la detección de
los umbrales, se realiza en la etapa de comparación, para ello, se configura
71
esta etapa para que calcule la diferencia de la conversión contra un valor de
Setpoint que para este caso lo dejaremos en cero, y configuramos los valores
umbrales(Threshold) con valores arbitrarios. Cada vez que la comparación
excede los valores umbrales, el flag ADTIF se activa, pudiendo generarse
interrupciones.
Periféricos Utilizados:
En esta práctica se utilizaran los pines GPIO para indicar los excesos a los
valores umbrales y el conversor ADC con su modulo computacional, desde el
cual podemos obtener los valores medios de la conversión y la comparación
con los niveles. En la siguiente figura se encuentra el modulo computacional,
donde se pueden ver las etapas de acumulación de valores y la de
comparación.
Configuración MCC:
72
73
El modulo ADC se configura en modo Burst_average_mode, con una fuente de
clock obtenido por la Frecuencia del Oscilador principal divido 4, las
referencias serán VDD y VSS y el resultado justificado a derecha.
74
El modulo computacional se configura por un lado, para que los valores de
Repeat y Acc Right Shift generen el valor medio de obtener 32 muestras de la
conversión y por el otro lado, se configura para que el comparador calcule la
resta entre el valor actual de la conversión contra un valor de Setpoint que fue
configurado en cero. Esta diferencia pasa luego al comparador que se activa si
el valor del resultado está por debajo o por encima de los valores umbrales.
Código:
#include "mcc_generated_files/mcc.h"
/*
Defines
*/
#define BUTTON_PRESSED 0
#define BUTTON_NOT_PRESSED 1
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
uint16_t valor_medio_adc = 0;
while (1)
{
// Add your application code
75
PIR1bits.ADTIF = 0;
}
else
{
D2_LAT = 0;
}
}
}
76
2) Luego configuramos el oscilador del sistema, en este caso volveremos
a seleccionar el oscilador interno como la fuente de Clock del sistema a
500KHz con el PLL deshabilitado.
77
4) Luego en la ventana de “Device Resources” seleccionamos haciendo
doble click el periférico Timer >> TMR0. Esto hará que el recurso del
timer 0 pase como recurso del proyecto y desplegará la ventana
central para configurar este timer.
78
6) Hacen click en Generate y cierran el MCC. Esto creara los archivos que
configuran el sistema, los pines GPIO y el timer 0. También generara un
main.c, lo abrimos y definimos la función main de la siguiente manera:
void main(void)
{
// initialize the device
SYSTEM_Initialize();
// Clear LED
D4_LAT = 0;
while (1) {
// Add your application code
// Wait for Timer0 to overflow
while(!TMR0IF);
// Toggle D4
D4_Toggle();
79
TMR0IF = 0;
}
}
80
2) Ejecutamos el MCC y comenzamos configurando el oscilador del
sistema. Nuevamente configuramos el oscilador interno a 1MHz y el
LVP activado.
81
la ventana PIN Manager seleccionamos el PIN RA4 como entrada
analógica y le ponemos el nombre POT.
82
5) Configuramos el Timer 2 con una fuente de reloj en FOSC/4, con el
Postscaler en 1 y el Prescaler en 1:2 y el periodo lo dejamos como esta,
en 2.048ms.
83
6) A continuación volvemos a la ventana de Recursos del Dispositivo y
buscamos el Periférico CCP >> CCP1. Esto se agregara a la ventana de
Recursos del Proyecto.
84
7) Luego seleccionamos el modo de trabajo del modulo CCP en PWM,
esto habilitara las configuraciones de este modulo. Configuramos para
que el timer 2 sea la fuente de clock del modulo, también
configuramos el duty Cycle para que sea del 50% al iniciar el modulo
PWM. Por último, la alineación debe estar a derecha, igual que la
alineación que seleccionamos en el conversor AD.
85
8) Una vez que tenemos el modulo PWM configurado, debemos
seleccionar el PIN RA3 como salida del modulo CPP5. Para esto vamos
al PIN manager y seleccionamos ese PIN como salida del CCP5.
86
9) Le ponemos como nombre a ese PIN D5, ya que controla el LED de la
placa.
87
10) Ya se configuraron todos los periféricos a utilizar en este proyecto, así
que apretamos el botón Generate para crear los archivos que
configuren el conversor ADC, el timer 2 y el modulo PWM. Abrimos el
main.c y copiamos el siguiente código.
void main(void)
{
// initialize the device
SYSTEM_Initialize();
// Start Timer2
TMR2_Start();
while (1) {
// Add your application code
// Start ADC conversion
ADCC_StartConversion(POT);
// Store the 2 LSbs into CCPR1L register to complete the 10bit resolution
CCPR5L = ADRESL;
//PWM5_LoadDutyValue(ADCC_GetConversionResult());
}
88
conversión, el resultado se asigna a los registros del PWM que
manejan el Duty Cycle de la señal, así en el registro CCPR5H se le
asigna el valor del registro ADRESH que es la parte alta del resultado y
para el registro CCPR5L se le asigna el registro ADRESL, la parte baja de
la conversión. Para que esto funcione adecuadamente es necesario
que ambos módulos, el conversor AD y el PWM, se configuren con la
misma alineación.
89
2) Crear un nuevo proyecto en MPLAB X y llamarlo “pwm_motor”
90
4) Luego vamos a configurar el conversor ADC para que tome la tensión
entregada por el potenciómetro de la placa. Agregamos el modulo ADC
desde la ventana de Device Resources y configuramos el ADC para que
la fuente de clock sea FOSC/4, la tensión de referencia positiva sea
VDD y la justificación de la conversión debe ser a derecha. Después en
la ventana PIN Manager seleccionamos el PIN RA4 como entrada
analógica y le ponemos el nombre POT.
91
5) Luego en la ventana de Recursos del Dispositivo, seleccionamos el
Timer >> TMR2 el cual funciona como fuente de clock del modulo
PWM.
92
6) Configuramos el Timer 2 con una fuente de reloj en FOSC/4, con el
Postscaler en 1 y el Prescaler en 1:2 y el periodo lo dejamos como esta,
en 2.048ms.
93
7) A continuación volvemos a la ventana de Recursos del Dispositivo y
buscamos el Periférico CCP >> CCP1. Esto se agregara a la ventana de
Recursos del Proyecto.
94
8) Luego seleccionamos el modo de trabajo del modulo CCP en PWM,
esto habilitara las configuraciones de este modulo. Configuramos para
que el timer 2 sea la fuente de clock del modulo, también
configuramos el duty Cycle para que sea del 50% al iniciar el modulo
PWM. Por último, la alineación debe estar a derecha, igual que la
alineación que seleccionamos en el conversor AD.
95
9) Una vez que tenemos el modulo PWM configurado, debemos
seleccionar el PIN RC7 como salida del modulo CPP5. Para esto vamos
al PIN manager y seleccionamos ese PIN como salida del CCP5.
96
10) Le ponemos como nombre a ese PIN D5, ya que controla el LED de la
placa.
97
11) Ya se configuraron todos los periféricos a utilizar en este proyecto, así
que apretamos el botón Generate para crear los archivos que
configuren el conversor ADC, el timer 2 y el modulo PWM. Abrimos el
main.c y copiamos el siguiente código.
void main(void)
{
// initialize the device
SYSTEM_Initialize();
// Start Timer2
TMR2_Start();
while (1) {
// Add your application code
// Start ADC conversion
ADCC_StartConversion(POT);
98
// Store the 2 LSbs into CCPR1L register to complete the 10bit resolution
CCPR5L = ADRESL;
//PWM5_LoadDutyValue(ADCC_GetConversionResult());
}
Descripción:
Tomaremos el código realizado en la primera clase la cual genera la inversión
en la secuencia de encendido de los LEDs pero en lugar de utilizar retardos que
99
no realizan ninguna acción, usaremos un temporizador, en este caso el timer 0,
para generar los tiempos de muestreo del pulsador y del encendido de los
LEDs. Esto será aprovechado para explicar uno de los recursos del
Microcontrolador, las interrupciones.
Periféricos utilizados:
Para esta práctica se utilizaran los pines GPIO para activar el encendido de los
LEDs de la placa y el uso del pulsador, también se utilizara el temporizador
Timer0 para generar los retardos de la aplicación y utilizaremos el modulo de
interrupciones. En la siguiente Figura podemos ver un diagrama en bloque del
Timer 0 en el PIC16F18855, ya que tiene algunas diferencias con los timers de
otros PIC.
100
Este Timer posee distintos modos de funcionamientos, en esta práctica
utilizaremos el modo 8 bits con recarga.
101
Configuración del MCC
102
103
Código:
#include "mcc_generated_files/mcc.h"
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
104
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable
bits
// Use the following macros to:
while (1)
{
// Add your application code
}
}
contador++;
105
if(contador == 10)
{
//reseteo contador
contador = 0;
if(rotateLeft)
{
rotateReg <<= 1;
if(rotateReg == 16)
rotateReg = 1;
}
else
{
rotateReg >>= 1;
if(rotateReg == 0)
rotateReg = 8;
}
LED_LoadRegister(rotateReg);
}
Al generarse los archivos desde el MCC, este crea el archivo tmr0.h, el cual
contiene la definición de la rutina de interrupción del Timer 0. Allí debemos
agregar el código a ejecutar cuando se generan las interrupciones. El código
agregado es básicamente el mismo código utilizado en la clase 1 para la
inversión de rotación, pero como ahora se genera en cada interrupción del
Timer, no es necesario utilizar la función de retardo, sino que simplemente
sabemos que esta porción de código se ejecuta en cada desborde del Timer.
Como en cada ejecución existen valores de variables que debemos conocer en
cada ejecución, se declaran variables del tipo estática, las cuales no se
destruyen al terminar la función, sino que retienen su valor para un siguiente
llamado de la función, de este modo podemos mantener los valores del
contador, el registro de rotación y la variable booleana que indica la dirección
de rotación.
106
por ello estas funciones fueron definidas y declaradas en los archivos
pin_manager.c y pin_manager.h.
Elementos Necesarios:
Para esta práctica es necesario el software TeraTerm para pode recibir los
caracteres que se transmiten desde el microcontrolador
107
3) Luego vamos a configurar el conversor ADC para que tome la tensión
entregada por el potenciómetro de la placa. Agregamos el modulo ADC
desde la ventana de Device Resources y configuramos el ADC para que
la fuente de clock sea FOSC/4, la tensión de referencia positiva sea
VDD y la justificación de la conversión debe ser a derecha. Después en
la ventana PIN Manager seleccionamos el PIN RC0 como entrada
analógica y le ponemos el nombre POT1.
108
4) A continuación vamos a la ventana de Recursos del Dispositivo y
buscamos el Periférico USART >> USART. Esto agregará el periférico
USART a la ventana de Recursos del Proyecto.
109
5) Configuramos la comunicación USART para que sea Asincrónico, con
un baud rate de 9600 y habilitamos la transmisión. También
habilitamos la redirección del STDIO (Standar Input/Output) hacia la
USART del micro, así de este modo podemos utilizar funciones como el
printf para escribir sobre la USART.
110
6) Luego seleccionamos los pines del Microcontrolador que se utilizaran
para la transmisión. El pin RC0 se utilizara para la transmisión (TX).
7) Una vez que se terminan de configurar los periféricos del PIC, hacemos
click en Generate, para que se realicen los fuentes del proyecto.
Abrimos el archivo main.c y colocamos el siguiente código.
111
void main(void)
{
// initialize the device
SYSTEM_Initialize();
while (1)
{
// Add your application code
uint16_t value_pot = ADCC_GetSingleConversion (POT1);
printf("Resultado Conversion = %d\r\n", value_pot);
}
}
112
113
PRÁCTICA 17: RECEPCIÓN USART
En esta práctica se verán los pasos para crear un proyecto en donde
realizaremos la transmisión y recepción de un carácter, simulando un eco local,
pero desde el PIC.
114
3) Luego seleccionamos el modulo USART de la ventana de Recurso del
Dispositivo. Allí seleccionamos el modo asincrónico para la USART,
configuramos el BaudRate a 9600 y habilitamos la transmisión y la
recepción continua. También activamos la redirección del STDIO a la
UART, para poder utilizar funciones de la stdio.h.
115
4) Luego configuramos los pines para la transmisión y recepción, en este
volvemos a seleccionar el pin RB5 para la recepción y el pin RB7 para la
transmisión.
116
void main(void) {
// initialize the device
SYSTEM_Initialize();
printf("Eco de caracteres\r\n");
while (1) {
// Add your application code
char caracter;
//Espero la llegada de un caracter
caracter = EUSART_Read();
//Lo escribo por la USART, simulando un eco
EUSART_Write(caracter);
}
}
117
118
PRÁCTICAS AVANZADAS
Descripción:
En esta práctica haremos uso de las técnicas de máquina de estados finitas
para obtener la conversión de diferentes canales del conversor ADC,
multiplexando los canales de entrada y que las conversiones no sean
bloqueantes, es decir que estas conversiones se puedan realizar en segundo
plano, mientras se realizan otras tareas.
Periféricos utilizados:
Para esta práctica se utilizaran los pines GPIO para activar el encendido de los
LEDs de la placa, el Timer 0 para generar los retardos y el conversor ADC.
119
120
Código:
#include "mcc_generated_files/mcc.h"
121
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
while (1)
{
// Add your application code
//Maquina de Estado del conversor ADC
switch(adc_state)
{
case ADC_STATE_START:
{
//Estado Inicial
//Si por algun motivo se llega a este estado y hay una conversion
//en proceso, espero a que este termine.
if(!ADCC_IsConversionDone())
break; //Salgo de la maquina sin cambiar de estado.
//Inicializamo estructura de datos
resultado_adc.channel_actual = POT;
122
break; //Salgo de la maquina sin cambiar de estado.
}
case ADC_STATE_END_CONVERTION:
{
//Transicion al siguiente estado
//Inicio conversion ADCC
ADCC_StartConversion(resultado_adc.channel_actual);
123
adc_state = ADC_STATE_CONVERT;
break;
}
default:
//Si la maquina de estados aparece en cualquier lugar la reseteo
adc_state = ADC_STATE_START;
}
//Pulsador
if(TMR0_HasOverflowOccured())
{
//Cada 50mseg.
if(BUTTON_Activated())
{
//Cambio de canal
switch(resultado_adc.channel_display)
{
case POT:
pADC_result = &resultado_adc.channel_anb0;
resultado_adc.channel_display = ANB0;
break;
case ANB0:
pADC_result = &resultado_adc.channel_anb1;
resultado_adc.channel_display = ANB1;
break;
case ANB1:
pADC_result = &resultado_adc.channel_anb5;
resultado_adc.channel_display = ANB5;
break;
case ANB5:
pADC_result = &resultado_adc.potenciometer;
resultado_adc.channel_display = POT;
break;
}
}
LED_LoadRegister((*pADC_result) >> 6);
124
inicial, el de conversión y el de fin de conversión. Luego se define una
estructura que guardara el resultado de las conversiones de los distintos
canales del AD y además contiene unas variables que guardan el canal actual
de conversión y el canal actual que se está mostrando en el display de LEDs.
125
Descripción:
Comenzaremos a ver la recepción de datos desde la USART utilizando el
método de polling explicado en la clase, donde haremos uso nuevamente de
una maquina de estado para recibir los caracteres y no generar tareas
bloqueantes y poder ejecutar procesos en paralelo. Veremos cómo recibir un
dato y detectar un carácter de control que indique el final del mensaje, que en
este caso será el la tecla Enter. Si es posible, se debe configurar el terminal
para que envié el carácter de final de línea solamente con un CR(Carry Return)
o un LF(Line Feed) y no con ambas, para simplificar el código.
Periféricos Utilizados:
En esta práctica se utilizaran los pines GPIO para mostrar el valor binario del
carácter hexadecimal recibido y el modulo USART para comunicarse con una
PC vía USB.
Configuración MCC:
126
Código:
#include "mcc_generated_files/mcc.h"
typedef enum
127
{
REC_INIT_STATE,
REC_WAIT_STATE,
REC_PROCESS_STATE
} RECEIVE_STATE;
//Variables Global
char receive_data;
//Prototipos
void StateMachine_ReceptionUSART(void);
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
printf("Ingrese un valor entre 0 y F y presione Enter\r\n");
while (1)
{
// Add your application code
//Corro la maquina de estados para recibir caracter.
StateMachine_ReceptionUSART();
128
else
led_data = receive_data - '0';
switch(rec_state)
{
case REC_INIT_STATE:
{
rec_state = REC_WAIT_STATE;
}
case REC_WAIT_STATE:
{
//Si no llego ningun caracter salgo sin cambiar el estado
if(!EUSART_DataReady) break;
data = getch();
//Borro flag de recepcion
PIR3bits.RCIF=0;
129
//Imprimo caracter recibido
putch(data);
//Cambio de estado
rec_state = REC_PROCESS_STATE;
}
case REC_PROCESS_STATE:
{
if(data == '\n' || data == '\r')
{
//Activo Flag
process_data = true;
}
else
{
receive_data = data;
}
}
default:
rec_state = REC_INIT_STATE;
}
}
130
Descripción:
En esta práctica veremos cómo realizar la transmisión y la recepción de
caracteres utilizando las interrupciones del PIC. Además veremos cómo realizar
el procesamiento de los datos recibidos para poder desglosar los campos y
quedarnos con los comandos. Hay que destacar que en la configuración del
MCC podemos seleccionar un buffer generado por software en donde se
guardan os valores hasta que realizamos la lectura. El tamaño del buffer es
configurable y se realiza mediante el MCC. Una vez que terminamos de
configurar el modulo USART por interrupciones en el MCC, vamos a hacer uso
de las rutinas de interrupción que genera este programa, ya que estas son
suficientes para recibir y transmitir datos, sin necesidad de realizar alguna
modificación.
Periféricos utilizados:
Para esta práctica se utilizaran los pines GPIO para activar el encendido de los
LEDs de la placa y el modulo de comunicación USART.
131
Configuración del MCC
132
133
Código:
#include "mcc_generated_files/mcc.h"
#include <string.h>
/**
Section: Defines
*/
#define BUFFER_SIZE 20
/**
Section: Prototypes
*/
bool ProcesarDatos(char* buffer_datos);
int GetStatusLed(char* aux);
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
134
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable
bits
// Use the following macros to:
}
}
}
135
Comenzaremos examinando el código de la función principal en donde se
habilitan las interrupciones globales y la de los perifericos. Una vez terminada
la configuración y activado las interrupciones, se comienza con el mensaje de
bienvenida y en el loop principal, se pregunta por el estado de la USART
consultando la constante EUSART_DataReady, provisto por la librería usart del
MCC. Cuando este devuelve true, indica que se recibió un carácter y por lo
tanto debemos leer el dato, si el dato es distinto al final de línea, se guarda en
un buffer. En cambio, si el dato recibido es un final de línea, entonces armo el
string, reseteo el contador de caracteres recibidos y realizo el procesamiento
de los datos.
//Divido el String
aux = strtok(buffer_datos, "=");
aux = strtok(NULL, "=");
if(aux == NULL)
return false;
if(strstr(buffer_datos,"LED1")!= NULL)
{
D2_LAT = GetStatusLed(aux);
}
else if(strstr(buffer_datos,"LED2")!= NULL)
{
D3_LAT = GetStatusLed(aux);
}
else if(strstr(buffer_datos,"LED3")!= NULL)
{
D4_LAT = GetStatusLed(aux);
}
else if(strstr(buffer_datos,"LED4")!= NULL)
{
D5_LAT = GetStatusLed(aux);
}
else
{
return false;
}
136
return true;
}
return LOW;
}
Cuando se termina de recibir el dato completo por parte del usuario, se llama a
la función ProcesarDatos() el cual toma el string y lo psa por la función strtok().
Esta función toma como parámetro un token, en esta caso se le pasa el signo
igual (=), y la función se encarga de buscar este token por el string y en cada
uno de los lugares encontrados, le coloca el carácter nulo, destruyendo el
string original. La función devuelve un puntero al string inicial y en cada
llamado sucesivo en donde se le pasa como parámetro el puntero nulo (NULL)
la función devolverá un puntero al siguiente string que se genero en la
separación, y así sucesivamente. De esta manera se logra separar, por ejemplo,
el comando LED1=ON en dos string, por un lado LED1 y por el otro ON. Luego
de separarlos, el primer string se compara con la función strstr() para saber a
que LED se esta accediendo, o si no reconoce ningún comando, la función
devuelve false. Si se encuentra a que LED se dirige el comando, luego la
función GetStatusLed(), devuelve true si la palabra es “ON” o false si es
cualquier otra palabra. Luego, de acuerdo al resultado de esta función, se
realiza el encendido o apagado del LED correspondiente.
Descripción:
137
Tomaremos el código realizado en la clase anterior para enviar comandos y le
agregaremos una validación inicial del tipo usuario-contraseña. Se debe
realizar primero una verificación inicial de la memoria, si este no tiene escrito
un nombre de usuario, se utilizara por defecto el par de nombres ‘admin-
admin’ de manera inicial. El programa no debe continuar con la ejecución
hasta que se hayan colocado correctamente los valores de usuario y
contraseña. Una vez que se supera esta validación, se ejecuta el programa
visto en la clase anterior donde se procesan comandos para encender los LEDs
individualmente. Para agregar la configuración de un usuario y una contraseña,
se debe agregar el comando “SETUP” desde el cual se desplegara en pantalla
una guía para configurar un nombre de usuario nuevo y una contraseña nueva.
Luego al pagar la placa y volver a encender, se debe ingresar los nuevos valores
de usuario y contraseña especificados anteriormente.
Periféricos utilizados:
Para esta práctica se utilizaran los pines GPIO para activar el encendido de los
LEDs de la placa, el uso de la USART para la comunicación con el usuario y la
memoria EEPROM interna para almacenar los datos de manera no volátil.
138
139
Código:
#include "mcc_generated_files/mcc.h"
#include <string.h>
/*
Defines
*/
#define BUFFER_SIZE 21
#define SIZE_STRING_USER_PASS 21
#define ADDRESS_USER_EEPROM_INT 0x7000
#define ADDRESS_PASS_EEPROM_INT ADDRESS_USER_EEPROM_INT +
SIZE_STRING_USER_PASS + 1
/*
Prototypes
*/
void ReadMemory(char* string, uint16_t address);
void WriteMemory(char* string, uint16_t address);
void RecibirMensajeUSART(char * receive_buffer);
bool CheckUserPass(char* user, char* pass);
void ConfigUserPass(void);
bool ProcesarDatos(char* buffer_datos);
int GetStatusLed(char* aux);
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
140
INTERRUPT_PeripheralInterruptEnable();
if( strlen(user) == 0)
{
//No hay usuario definido, uso usuario Default
strcpy(user, "admin");
strcpy(pass, "admin");
}
//DEBUG
printf("**DEBUG** user:%s pass:%s\r\n", user, pass);
//Mensaje inicial
printf("*-------Recepcion de Comandos por USART------*\r\n\r\n");
//Nos quedamos en este loop hasta recibir al usuario correcto
while(!CheckUserPass(user, pass));
while (1)
{
// Add your application code
if(EUSART_DataReady)
{
//Guardar caracter
char data = getch();
if(data == '\r' || data == '\n')
{
//Fin del mensaje, termino el string
receive_buffer[buffer_count] = '\0';
//reinicio contador de caracteres
buffer_count = 0;
//Procesar datos
if(!ProcesarDatos(receive_buffer))
printf("\r\nComando no valido\r\n");
141
else
printf("\r\n");
}
else
{
receive_buffer[buffer_count++] = data;
putch(data);
}
}
}
}
Funciones Auxiliares
void RecibirMensajeUSART(char * receive_buffer)
{
int buffer_count = 0;
char data;
//Comienzo con la recepcion de datos
do
{
while(!EUSART_DataReady);
//Guardar caracter
data = getch();
if(data == '\r' || data == '\n')
{
//Fin del mensaje, termino el string
142
receive_buffer[buffer_count] = '\0';
//reinicio contador de caracteres
buffer_count = 0;
}
else
{
receive_buffer[buffer_count++] = data;
putch(data);
}
if(!strcmp(user, user_receive))
{
if(!strcmp(pass, pass_receive))
{
printf("\r\nUsuario y contraseña Correcta!\r\n");
return true;
}
else
{
printf("\r\nContraseña Incorrecta!\r\n");
return false;
}
}
else
143
{
printf("\r\nUsuario Incorrecto!\r\n");
return false;
}
}
void ConfigUserPass(void)
{
char user_receive[SIZE_STRING_USER_PASS];
char pass_receive[SIZE_STRING_USER_PASS];
char data;
}
Esta es la función encargada de tomar el nuevo nombre de usuario y
contraseña y guardarla en la memoria EEPROM. Mediante simples mensajes se
va guiando al usuario a configurar un usuario y una contraseña nueva. Una vez
144
ingresado ambos valores, se consulta si se desea ejecutar la acción de
guardarlo en memoria oprimiendo la tecla “Y” en la PC. Si es así, se inicia la
secuencia de escritura en la EEPROM interna, donde se pasa los strings de
usuario y contraseña a la función WriteMemory()
do
{
//incrmento indice, el primer valor sera 0
indice++;
//Utilizo funcion de lectura por byte de la EEPROM interna
data = DATAEE_ReadByte(address + indice);
string[indice]= data;
}while((data != 0)&& (data != 0xFF) );
do
{
indice++;
//Utilizo funcion de escritura por byte de la EEPROM interna
DATAEE_WriteByte(address + indice, string[indice]);
//copio tambien el caracter nulo en la memoria EEPROM
}while(string[indice]);
}
145
Las funciones ReadMemory() y WriteMemory() son las encargadas de leer un
String y de escribir un String en la memoria EEPROM interna. Para leerlo hacen
uso de las funciones de la librería del MCC para lectura y escritura de un byte
en la EEPROM, las funciones son DATAEE_ReadByte() y DATEE_WriteByte().
Cabe aclarar que la función de escritura implementada copia el carácter nulo
en la memoria y la función de lectura devuelve el string con el carácter nulo al
final.
//Divido el String
aux = strtok(buffer_datos, "=");
aux = strtok(NULL, "=");
if(aux == NULL)
return false;
if(strstr(buffer_datos,"LED1")!= NULL)
{
D2_LAT = GetStatusLed(aux);
}
else if(strstr(buffer_datos,"LED2")!= NULL)
{
D3_LAT = GetStatusLed(aux);
}
else if(strstr(buffer_datos,"LED3")!= NULL)
{
D4_LAT = GetStatusLed(aux);
}
else if(strstr(buffer_datos,"LED4")!= NULL)
{
D5_LAT = GetStatusLed(aux);
}
else
{
return false;
}
146
return true;
}
return LOW;
}
Estas son las funciones que procesan los comandos que envía el usuario una
vez pasado la verificación inicial de usuario. La función es exactamente igual a
la vista en la práctica pasada, pero con el agregado de procesar la palabra
SETUP, en cuyo caso ejecutará la rutina ConfigUserPass() para agregar un
nuevo usuario y contraseña.
Descripción
Para la realización de esta práctica haremos uso del conversor ADC trabajando
en modo básico para tomar muestras del sensor analógico. Las muestras
deben ser periódicas, y se realizaran cada dos milisegundos, por lo que se hará
uso del timer 1 para generar interrupciones periódicas. Cada muestra sucesiva
del sensor se almacena en un array y se detecta el pico de la señal, al detectar
donde se producen los picos, se calcula el tiempo que transcurre entre estos
picos, y mediante una simple cuenta, se obtienen las pulsaciones por minuto.
Periféricos Utilizados:
En esta práctica se utilizaran el conversor ADC en el modo básico y el Timer 1
para generar retardos periódicos cada 2mSegundos. También se utilizara el
modulo de interrupciones.
147
148
149
150
Código:
#include "mcc_generated_files/mcc.h"
// Variables
extern volatile int BPM; // int that holds raw Analog in 0. updated every 2mS
extern volatile int IBI;
extern volatile bool QS; // becomes true when Pic finds a beat.
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
// When using interrupts, you need to set the Global and Peripheral Interrupt Enable
bits
// Use the following macros to:
while (1)
{
// Add your application code
//printf("loop");
while(QS == false);
__delay_ms(1000);
//D4_Toggle();
}
}
151
En el archivo main.c se inicia declarando algunas variables externas, estas
variables son globales y están definidas en el archivo tmr1.c, que maneja las
interruciones del Timer. Estas variables almacenan el pulso cardiaco (BPM) el
intervalo de tiempo entre picos (IBI) y la variable que indica que ya están los
datos disponibles (QS). Luego en el Loop Principal, simplemente se pregunta
cada 1 segundo si los datos están disponibles y los imprime en pantalla.
// Variables
void TMR1_DefaultInterruptHandler(void){
// add your TMR1 interrupt custom code
// or set custom function using TMR1_SetInterruptHandler()
INTCONbits.GIE = 0; // disable interrupts while we do this
TMR1_StopTimer();
TMR1_Reload();
//TMR1_StartTimer();
Signal = ADCC_GetSingleConversion(channel_pulse_heart);
152
// read the Pulse Sensor
sampleCounter += 8;
// keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime;
// monitor the time since the last beat to avoid noise
//printf("IBI:%i-N:%i\r\n",IBI, N);
if(secondBeat){
// if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){
// seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){
153
// if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
INTCONbits.GIE = 1; // enable interrupts again
TMR1_StartTimer();
return; // IBI value is unreliable so discard it
}
154
T = 512; // set T default
lastBeatTime = sampleCounter;
// bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
PROYECTO INTEGRADOR
Introducción:
Haremos uso de los conocimientos adquiridos durante el curso para realizar un
sistema de monitoreo de temperatura inteligente utilizando el dispositivo
EMC1001, el cual nos permite no solo monitorear la temperatura de un
equipo, sino que además podemos configurarles valores de temperaturas
Limites con la cual accionar algún sistema de refrigeración si existe algún
sobrecalentamiento.
155
configurar en valor de Temperatura Limite, el cual si es superado, debe
activarse un sistema de refrigeración, que en este caso será emulado por un
LED.
Descripción:
Utilizaremos los recursos que provee el sensor de temperatura EMC1001, el
cual se comunica por el protocolo de comunicación serie SMBus. Con este
dispositivo no solo podemos obtener el valor actual de la temperatura, sino
que también podemos configurarle un valor de temperatura limite, en el cual,
si la medición supera este valor, se activara el pin de ALARM-/THERM el cual
puede utilizarse para encender un sistema de refrigeración para controlar la
temperatura del dispositivo. Para la práctica vamos a detectar si se activo esta
alarma mediante el sensado del estado en el pin RA7 del PIC e indicarlo con un
LED el estado de la activación. Para detectar el cambio de estado del pin
ALARM-/THERM, vamos a configurar una interrupción por cambio de estado
(IOC) en el pin RA7. Las comunicaciones con el modulo EMC1001 serán por
medio del protocolo SMBus, que es muy similar al I²C, y cuya dirección de
esclavo es el valor H38. Este valor es sacado de la hoja de datos junto con el
valor de la resistencia de pull-up en el pin ALARM-/THERM. Mediante lecturas
vamos a leer el estado del conversor, cabe destacar que el código de lectura
del estado lo obtuvimos desde un código fuente en los ejemplos realizados por
la comunidad de Microchip para el MPLABXpress. También agregaremos la
posibilidad de configurar el valor de temperatura limite, llamado THERM Limit
y su valor de hysteresis para la activación y desactivación de la alarma de
temperatura. En la siguiente figura se puede ver el comportamiento del pin
ALARM-/THERM cuando la temperatura supera el umbral limite y el
funcionamiento de la hysteresis.
156
Periféricos utilizados:
Para esta práctica se utilizaran los pines GPIO para activar el encendido de los
LEDs de la placa y poder detectar el cambio de estado en el pin ALARM-
/THERM del modulo, además se utilizara la USART para la interfaz de ususario
y el modulo MSSP en modo I²C para la comunicación con el EMC1001.
157
158
159
160
Código:
#include "mcc_generated_files/mcc.h"
#include <stdlib.h>
uint16_t emc_1001_address;
#define SIZE_STRING_BUFFER 20
// EMC1001 registers
#define TEMP_HI 0x00 // temperature value high byte
#define TEMP_LO 0x02 // low byte containing 1/4 deg fraction
#define TEMP_HIGH_LIMIT_H 0x05
#define TEMP_HIGH_LIMIT_L 0x06
#define TEMP_LOW_LIMIT_H 0x07
#define TEMP_LOW_LIMIT_L 0x08
#define THERM_LIMIT 0x20
#define THERM_HYSTERESIS 0x21
161
bool EMC1001_Read(uint8_t reg, uint8_t *pData);
bool EMC1001_Write(uint8_t reg, uint8_t data);
//Menu
void ImprimirMenu(void);
void ImprimirEstado(void);
void MonitorearTemperatura(void);
void ConfigThermLimit(void);
void RecibirMensajeUSART(char * receive_buffer);
void main(void)
{
SYSTEM_Initialize();
INTERRUPT_GlobalInterruptEnable();
INTERRUPT_PeripheralInterruptEnable();
char data;
emc_1001_address = EMC1001_20K_ADDRESS;
while (1)
{
ImprimirMenu();
data = getch();
switch(data)
{
case '1':
ImprimirEstado();
break;
case '2':
ConfigThermLimit();
break;
case '3':
MonitorearTemperatura();
break;
default:
printf("Opcion Incorrecta\r\n");
}
}
}
El archivo fuente del programa principal comienza con algunas definiciones
útiles para el desarrollo del programa, como la dirección del EMC1001 y los
registros del modulo. En la función principal se comienza asignando la
dirección del modulo EMC1001 en una variable global. Luego en el bucle
162
principal se imprime el menú de opciones y se espera, mediante la función
getch(), a que el usuario coloque una opción, luego esta se procesa mediante
el bloque condicional switch. En cada una de las opciones se ejecutan las
funciones correspondientes y una vez finalizado, se vuelve a imprimir el menú.
Funciones Auxiliares
void ImprimirMenu(void)
{
printf("\r\n*MENU*\r\n\r\nPresione una opcion:\r\n1-Imprimir Estado\r\n2-Setear
Temperatura Limite\r\n3-Monitor de Temperatura\r\n");
}
void ImprimirEstado(void)
{
uint8_t data;
int8_t temp;
uint8_t templo;
if (EMC1001_Read(TEMP_HI, (uint8_t*)&temp)) {
EMC1001_Read(TEMP_LO, &templo); // get lsb
templo = templo >> 6;
if (temp < 0) templo = 3-templo; // complement to 1 if T negative
printf("\n\rThe temperature is: %d.%d C\n\r", temp, templo*25);
}
163
La primera función simplemente imprime el menú por la USART, la segunda, es
una función tomada de un ejemplo que se encuentra en el MPLABXpress, el
cual imprime en pantalla el estado general del modulo EMC1001, utilizando la
función de lectura del modulo.
void MonitorearTemperatura(void)
{
char data = '0';
int8_t temp;
uint8_t templo;
do
{
if (EMC1001_Read(TEMP_HI, (uint8_t*)&temp)) {
EMC1001_Read(TEMP_LO, &templo); // get lsb
templo = templo >> 6;
if (temp < 0) templo = 3-templo; // complement to 1 if T negative
printf("\rThe temperature is: %d.%d C ", temp, templo*25);
}
__delay_ms(1000);
if(EUSART_DataReady)
{
data = getch();
}
} while (data != 'Q' && data != 'q');
void ConfigThermLimit(void)
{
char buffer[SIZE_STRING_BUFFER];
164
char data;
EMC1001_Write(THERM_LIMIT, (uint8_t)limit);
EMC1001_Write(THERM_HYSTERESIS, (uint8_t)hysteresis);
165
I2C2_MasterReadTRBBuild(&trb[1], pData, 1, emc_1001_address);
I2C2_MasterTRBInsert(2, &trb[0], &status);
uint8_t send_data[3];
send_data[0] = reg;
send_data[1] = data;
166
PROYECTOS CON PLACAS CLICK
WEATHER CLICK
La placa Weather Click está diseñada para obtener distintos parámetros
ambientales que indican el estado del clima. Esta placa cuenta con el sensor
BME280 del fabricante Bosch y permite realizar mediciones de presión,
humedad y temperatura. Está diseñado para trabajar con bajo consumo y con
largos periodos de estabilidad, trabaja con una tensión de alimentación de
3,3V. El integrado es totalmente digital, los resultados de las mediciones de
humedad, presión y temperatura son almacenados en registros internos, junto
con otros registros de configuraciones. El modo de comunicarse con este
sensor es utilizando el protocolo SPI o el I2C.
Descripción general
El uso de este sensor es ideal en aplicaciones que requieran el estado del clima
en un determinado ambiente utilizando los parámetros de humedad, presión y
temperatura. Las posibles aplicaciones se encuentran en la automatización de
los hogares inteligentes, in-door navigation, fitness o como información
suplementaria en las estaciones de GPS.
167
trabaja sobre un gran rango de temperaturas. El sensor de temperatura cuenta
además con una alta resolución e inmunidad a los ruidos. La salida de
temperatura se utiliza para compensar los valores de humedad y presión o
para registrar la temperatura ambiente.
- Modo Sleep.
- Modo Normal.
- Modo Forzado manual.
NC 1 AN PW 16 NC
M
NC 2 RST INT 15 NC
Chip Select CS 3 CS TX 14 NC
SPI Clock SCK 4 SCK RX 13 NC
SPI Master Input Slave MIS 5 MISO SCL 12 SCL I2C Clock
Output O
SPI Master Output MOS 6 MOSI SDA 11 SDA I2C Data
Slave Input I
Power supply 3,3V 7 3,3V 5V 10 NC
Ground GND 8 GND GND 9 GND Ground
168
recomienda que la lectura de todos los valores se realice en un solo comando
de lectura o como lo indica el fabricante, realizar una lectura del tipo “burst”,
ya que de esta manera nos aseguramos que no ocurran cambios en los
registros durante un proceso de lectura. Además es posible agregar en las
mediciones de la presión y de la temperatura un filtro IIR a la salida, utilizado
para quitar las fluctuaciones que pueden aparecer en un cuarto cerrado, por
ejemplo, donde el cierre o apertura de una puerta puede generar una mínima
modificación en la lectura de la presión. El fabricante del sensor aconseja
realizar una compensación a los valores medidos por el mismo, para ello se
hace uso de una fórmula para cada uno de las magnitudes a medir junto con
un conjunto de valores que son almacenados en una memoria no volátil en el
chip y que son valores ajustados de fabrica. Para dispositivos de 8 bits, las
formulas son las siguientes:
Temperatura
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
BME280_S32_t t_fine;
BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T)
{
BME280_S32_t var1, var2, T;
var1 = ((((adc_T>>3) – ((BME280_S32_t)dig_T1<<1))) * ((BME280_S32_t)dig_T2)) >> 11;
var2 = (((((adc_T>>4) – ((BME280_S32_t)dig_T1)) * ((adc_T>>4) – ((BME280_S32_t)dig_T1))) >> 12) *
((BME280_S32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
Presión
// Returns pressure in Pa as unsigned 32 bit integer. Output value of “96386” equals 96386 Pa = 963.86 hPa
BME280_U32_t BME280_compensate_P_int32(BME280_S32_t adc_P)
{
BME280_S32_t var1, var2;
BME280_U32_t p;
var1 = (((BME280_S32_t)t_fine)>>1) – (BME280_S32_t)64000;
var2 = (((var1>>2) * (var1>>2)) >> 11 ) * ((BME280_S32_t)dig_P6);
var2 = var2 + ((var1*((BME280_S32_t)dig_P5))<<1);
var2 = (var2>>2)+(((BME280_S32_t)dig_P4)<<16);
var1 = (((dig_P3 * (((var1>>2) * (var1>>2)) >> 13 )) >> 3) + ((((BME280_S32_t)dig_P2) * var1)>>1))>>18;
var1 =((((32768+var1))*((BME280_S32_t)dig_P1))>>15);
if (var1 == 0)
{
return 0; // avoid exception caused by division by zero
}
169
p = (((BME280_U32_t)(((BME280_S32_t)1048576)-adc_P)-(var2>>12)))*3125;
if (p < 0x80000000)
{
p = (p << 1) / ((BME280_U32_t)var1);
}
else
{
p = (p / (BME280_U32_t)var1) * 2;
}
var1 = (((BME280_S32_t)dig_P9) * ((BME280_S32_t)(((p>>3) * (p>>3))>>13)))>>12;
var2 = (((BME280_S32_t)(p>>2)) * ((BME280_S32_t)dig_P8))>>13;
p = (BME280_U32_t)((BME280_S32_t)p + ((var1 + var2 + dig_P7) >> 4));
return p;
}
Humedad
// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
// Output value of “47445” represents 47445/1024 = 46.333 %RH
BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H)
{
BME280_S32_t v_x1_u32r;
v_x1_u32r = (t_fine – ((BME280_S32_t)76800));
v_x1_u32r = (((((adc_H << 14) – (((BME280_S32_t)dig_H4) << 20) – (((BME280_S32_t)dig_H5) * v_x1_u32r))
+
((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r *
(BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) *
((BME280_S32_t)dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r – (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)dig_H1)) >>
4));
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
return (BME280_U32_t)(v_x1_u32r>>12);
}
Interfaz de comunicación
El sensor BME280 soporta tanto la interfaz digital SPI como el I2C. En ambos
protocolo, el sensor actuara como el dispositivo esclavo. La interfaz SPI soporta
los modos ‘00’ (CPOL = CPHA = ‘0’) y el modo ‘11’ (CPOL = CPHA = ‘1’) ya sea
utilizando 4 o 3 hilos en la comunicación. La interfaz I2C soporta las velocidades
Standart, Fast y High Speed.
170
- Lectura de múltiples Bytes (se envía una Dirección Base y luego se
autoincrementa para la lectura)
La placa Weather cuenta con unos Jumpers que permiten seleccionar el tipo de
comunicación, I2C o SPI. Por defecto, los jumpers están colocados para
trabajar en I2C, por lo que este protocolo será utilizado en las prácticas de este
libro.
Interfaz I2C
La interfaz esclava I2C es compatible con la especificación de Philips versión
2.1, soportando los modos de comunicación standart, fast y high speed. Los 7-
bit de dirección del dispositivo son 111011x, donde los 6 bits más significativos
son fijos y el ultimo bit esta dado por el estado del pin SDO y que además
puede ser cambiado durante la operación del dispositivo. Si se conecta el pin
SDO a GND, la dirección esclava del dispositivo será 1110110 (0x76); en
cambio, si el pin es conectado a VDD, la dirección resultante será 1110111
(0x77). Los pines utilizados en la interfaz I2C son los siguientes:
El pin CSB debe estar conectado a VDD para seleccionar la interfaz I2C. En la
hoja de datos del BME280 se puede visualizar el mapa de memoria de los
registros del sensor.
171
Descripción
Ya vimos cuales son las características y modo de operaciones con las que se
puede configurar el sensor BME280, el cual utilizaremos en esta práctica para
medir la humedad, presión y temperatura de un recinto. En cuanto a las
necesidades de Hardware para esta práctica, vamos a utilizar la placa MPLAB
Xpress junto con la placa “Weather Click” de la empresa Mikroelectonika. La
placa Weather está provista de unos jumpers que permiten seleccionar la
interfaz de comunicación entre el sensor y el microcontrolador, por defecto,
estos jumpers están configurados para trabajar con la interfaz I2C.
Periféricos utilizados
La comunicación entre el sensor y el microcontrolador es a trabes de la
interfaz I2C, por lo que haremos uso de esta interfaz de comunicación y
realizaremos su configuración tal como lo vimos en prácticas anteriores. Para
mostrar los resultados de las magnitudes que nos entrega el sensor haremos
uso de la comunicación USART y mostrar los datos en la PC.
172
En la versión 3.45.1 del MPLAB Code Configurator se agregan como soporte
algunas de las placas Click de la empresa MikroElectronika, en particular, se
agrega el soporte a la placa Weather Click. Para agregarla a nuestro proyecto,
se debe recorrer la ventana “Device Resources” ir a la sección “Mikro-E Clicks”
y buscar entre las placas “Sensors” la que vamos a utilizar, la placa “Weather”,
una vez encontrada, hacer doble click sobre ella para agregarla a la ventana de
Recursos del proyecto.
173
Una vez agregado la librería de la placa Weather, se agregaran los códigos
fuentes que manejan la los registros y la comunicación I2C con la placa. En
todos los casos, dejar las configuraciones por defecto, pero activar en la
sección “Example” el casillero que dice “Generate Example”.
174
175
Por último agregar el módulo USART para la comunicación con la PC y poder
mostrar los resultados en pantalla.
176
Una vez que terminamos la configuración hacemos click en “Generate” dentro
de la ventana de recursos del proyecto y el MCC generará los siguientes
códigos fuentes que controlaran el sensor BME280 de la placa Weather.
Código principal
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/bme280.h"
#include "mcc_generated_files/weather_example.h"
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
//Mensaje de Bienvenida
printf("----- Weather Click -----\r\n");
177
printf("BME280 Chip ID: %i\r\n", chip_id);
while (1)
{
// Add your application code
__delay_ms(1000);
Weather_example();
}
}
Funciones Auxiliares
void Weather_example(void) {
// Read the sensor data registers
Weather_readSensors();
178
// Return the compensated measurement readings of each sensor
printf("T = %i C \t", (int8_t) Weather_getTemperatureDegC());
printf("P = %u kPa \t", (uint8_t) Weather_getPressureKPa());
printf("H = %u RH \n\r", (uint8_t) Weather_getHumidityRH());
}
La rutina Weather_example() está provista por la librería del MCC para la placa
Weather, y se define su código en el archivo weather_example.c. La rutina
comienzo realizando el proceso de medición, llamando al método
Weather_readSensors() el cual inicia de manera forzada una medición en el
sensor, espera a que este termine y almacena los resultados en variables
utilizadas por la librería. Luego los valores son mostrados por USART utilizando
a función printf() y tomando los valores, ya compensados, de la temperatura,
presión y humedad. Cada uno de las funciones
Weather_getTemepratureDegC(), Weather_getPressureKPa() y
Weather_getHumidityRH() devuelven los valores, ya compensados de cada una
de las magnitudes. Debido a que el micro solo tiene un procesador de 8 bits,
los resultados obtenidos se manejan como punto fijo de 32 bits, tal como se
indico en la sección de compensación de los resultados obtenidos por el
sensor.
6 DOF IMU 2
Esta placa Click está diseñada para medir la aceleración en tres ejes y los
valores de giroscopio, también en sus tres ejes. Cuenta con el Sensor BMI160
de la empresa Bosch de bajo consumo para ser utilizado como una unidad de
medición inercial (IMU en sus siglas en ingles). Los IMU son utilizados en
sistemas de navegación inercial y funcionan detectando la aceleración en uno
o más ejes y los atributos rotacionales, usando giroscopios en uno o más ejes.
El sensor BMI160 cuenta con una unidad administradora de potencia (PMU)
para una eficiente administración de la energía y de los estados de bajo
consumo para aumentar el rendimiento de baterías. Se alimenta con 3,3V y se
comunica con el procesador principal utilizando los protocolos de
comunicación SPI o I2C.
Descripción General
El sensor BMI160 es una unidad de medición inercial conformado por un
acelerómetro low-g de 3 ejes y un giroscopio de bajo consumo de 3 ejes,
formando una unidad de medición de 6 ejes, que puede ser extendido a 9, ya
179
que cuenta con una segunda interfaz que puede ser conectado a un
magnetómetro externo de 3 ejes. Este sensor está diseñado para ser utilizado
en tablets, teléfonos móviles, control remoto, juegos y en juguetes. En el modo
normal de funcionamiento puede llegar a consumir unos 925µA y trabaja a una
tensión de 3,3V resultando útil en sistemas alimentados por batería. Cuenta
con FIFO de 1024 posiciones que permite almacenar los últimos valores.
Además cuenta con un sistema de interrupciones que permite detectar gestos
basados en el movimiento y de reconocimiento del contexto. Mediante el
módulo de interrupción es posible detectar si el sistema está en movimiento o
no, si se realizo un toque o un doble toque, detección de la orientación, caída
libre o detectar algún evento de choque. Existen tres modos de operación
soportados para el acelerómetro y el giroscopio:
Acelerómetro:
Giroscopio
NC 1 AN PW 16 NC
M
NC 2 RST INT 15 INT Interrup
ción
Chip Select CS 3 CS TX 14 NC
SPI Clock SCK 4 SCK RX 13 NC
SPI Master Input Slave MISO 5 MISO SCL 12 SCL I2C
180
Output Clock
SPI Master Output MOSI 6 MOSI SDA 11 SDA I2C Data
Slave Input
Power supply 3,3V 7 3,3V 5V 10 NC
Ground GND 8 GND GND 9 GND Ground
FIFO
El sensor BMI160 cuenta con una FIFO interna que permite almacenar datos
que no fueron leídos, evitando que estos se pierdan si se trabaja en sistemas
de tiempo real. La FIFO tiene un tamaño de 1024bytes y sirve para alojar tanto
datos del acelerómetro como del giroscopio. Si se agrega un sensor
181
geomagnético externo, la FIFO soporta la capacidad de almacenar los datos de
ese sensor también.
Controlador de Interrupciones
Existen dos pines de interrupciones que pueden ser utilizado para señalizar
cualquiera de las trece fuentes de interrupciones que posee el sensor. La salida
de estas interrupciones pueden ser programadas por parte del usuario.
182
Interfaz de comunicación
El sensor BMI160 soporta tanto la interfaz digital SPI como el I2C como
interface de comunicación primaria con el host y cuenta con una segunda
interfaz para un sensor externo. En la interfaz primaria, en ambos protocolo, el
sensor actuará como el dispositivo esclavo. La interfaz SPI soporta los modos
‘00’ (CPOL = CPHA = ‘0’) y el modo ‘11’ (CPOL = CPHA = ‘1’) ya sea utilizando 4 o
3 hilos en la comunicación. La interfaz I2C soporta las velocidades Standart,
Fast y High Speed.
La placa 6DOF IMU 2 cuenta con unos Jumpers que permiten seleccionar el
tipo de comunicación, I2C o SPI. Por defecto, los jumpers están colocados para
trabajar en I2C, por lo que este protocolo será utilizado en las prácticas de este
libro.
Interfaz I2C
La interfaz esclava I2C es compatible con la especificación de Philips versión
2.1, soportando los modos de comunicación standart, fast y high speed. Los 7-
bit de dirección del dispositivo son 110100x, donde los 6 bits más significativos
son fijos y el ultimo bit esta dado por el estado del pin SDO y que además
puede ser cambiado durante la operación del dispositivo. Si se conecta el pin
SDO a GND, la dirección esclava del dispositivo será 1101000 (0x68); en
cambio, si el pin es conectado a VDD, la dirección resultante será 1101001
(0x69). Los pines utilizados en la interfaz I2C son los siguientes:
183
- SDI: data (SDA)
- SDO: Bit menos significativo de la dirección esclava (GND = ‘0’, Vdd =
‘1’)
El pin CSB debe estar conectado a VDD para seleccionar la interfaz I2C. En la
hoja de datos del BMI160 se puede visualizar el mapa de memoria de los
registros del sensor.
Descripción
Ya vimos cuales son las características y modo de operaciones con las que se
puede configurar el sensor BMI160, el cual utilizaremos en esta práctica para
medir la aceleración y velocidad angular de un dispositivo. En cuanto a las
necesidades de Hardware para esta práctica, vamos a utilizar la placa MPLAB
Xpress junto con la placa “6DOF IMU 2 Click” de la empresa Mikroelectonika.
La placa está provista de unos jumpers que permiten seleccionar la interfaz de
comunicación entre el sensor y el microcontrolador, por defecto, estos
jumpers están configurados para trabajar con la interfaz I2C. El
microcontrolador actuará como el maestro de la comunicación y la dirección
del esclavo, en este caso el sensor BMI160, será 0x68 en hexadecimal, ya que
el pin SDO viene conectado a masa por defecto en la placa Click.
Periféricos utilizados
La comunicación entre el sensor y el microcontrolador es a trabes de la
interfaz I2C, por lo que haremos uso de esta interfaz de comunicación y
realizaremos su configuración tal como lo vimos en prácticas anteriores. Para
mostrar los resultados de las magnitudes que nos entrega el sensor haremos
uso de la comunicación USART y mostrar los datos en la PC.
184
Clock en 4, para un reloj del sistema trabajando a 1MHz, igual que en las
prácticas anteriores, como lo muestra la siguiente figura.
185
De acuerdo a la distribución de pines que tiene la placa 6 DOF IMU 2 para su
comunicación I2C y los pines de comunicación USART, en la ventana PIN
Module se debe tener la siguiente configuración.
186
la EUSART. Hasta la presente edición del libro, todavía no se han agregado en
el MPLAB Code Configurator una librería de control para la placa 6 DOF IMU 2,
como si lo tiene la placa Weather. Pero el fabricante de las placas nos provee
de ejemplos como para poder utilizarlo, de allí se pudo modificar la librería de
control del sensor BMI160 para que pueda utilizarse con el compilador XC8 y
que será provisto durante el curso como material para que los alumnos
puedan utilizarlo y controlar el sensor mediante un set de funciones.
187
Estas son las tres funciones que debemos modificar para que la librería
funcione en nuestro ejemplo. Luego se deben agregar estos códigos fuentes al
proyecto, por lo que quedaría de la siguiente manera:
Código principal:
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/6dof_hal.h"
#include "mcc_generated_files/6dof_hw.h"
/*
Prototypes
*/
void display_data();
/*
Main application
*/
void main(void)
{
// initialize the device
SYSTEM_Initialize();
//Mensaje de Bienvenida
printf("----- 6DOF IMU 2 Click -----\r\n");
188
//Valor del Chip_ID para test
uint8_t chip_id;
dof6_hal_init(BMI160_I2C_ADDRESS );
chip_id = dof6_get_chip_id();
printf("BMI160 Chip ID: %i\r\n", chip_id);
Funciones Auxiliares
void display_data()
{
float acc_x,acc_y,acc_z,gyro_x,gyro_y,gyro_z;
acc_x=dof6_get_acc_x();
acc_y=dof6_get_acc_y();
acc_z=dof6_get_acc_z();
gyro_x=dof6_get_gyr_x();
gyro_y=dof6_get_gyr_y();
gyro_z=dof6_get_gyr_z();
189
printf("acc x = %i \t gyro x: %i \r\n", (int8_t) acc_x, (int8_t) gyro_x);
printf("acc y = %i \t gyro y: %i \r\n", (int8_t) acc_y, (int8_t) gyro_y);
printf("acc z = %i \t gyro z: %i \r\n", (int8_t) acc_z, (int8_t) gyro_z);
Una vez escrito todo el código y agregado las librerías modificadas para esta
plataforma, se puede compilar el proyecto y cargarlo en la placa MPLAB
Xpress. Una vez que arranca el programa, se comienza a escribir en pantalla el
valor de las aceleraciones y el giroscopio.
190
191
Se terminó de imprimir
en Agosto de 2018
en Rolta, (4865-7337),
Ecuador 334, Buenos Aires.
www.rolta.com.ar
192