Documentos de Académico
Documentos de Profesional
Documentos de Cultura
CAPITULO 2
La programación en C
Idioma
C
C. El lenguaje que a todos nos encanta odiar. Hemos visto C++, Java, C#, Go, Python
y muchos otros lenguajes ir y venir. Sin embargo, C permanece. C fue, es y será. El
ensamblador solía ser el lenguaje elegido para programar microcontroladores de 8
bits. Sin embargo, los microcontroladores PIC® más nuevos tienen una arquitectura
Programación en C
El lenguaje de programación C fue diseñado en un momento en que el poder de
cómputo era una fracción de lo que es hoy. Olvídese de los gigabytes de memoria,
había kilobytes. Olvídese de los gigahercios de velocidad informática, aquí estamos
hablando de megahercios. Suena familiar, ¿no? kilobytes de
15
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_2
Machine Translated by Google
Los programas en C tienen una estructura que deben seguir. El listado 2-1 es
un programa C típico.
// Aquí es donde se incluyen los archivos y se llevan a cabo las directivas del
preprocesador
} // cerrar paréntesis
dieciséis
Machine Translated by Google
Comentarios
/* Esto es un comentario
eso puede ser usado
Los comentarios también se pueden escribir con dos barras diagonales y estos
Variables y constantes
17
Machine Translated by Google
int A = 10;
18
Machine Translated by Google
23 *cifras significativas.
52 *cifras significativas.
En C, una matriz es un tipo de estructura de datos que puede almacenar varios elementos,
conocidos como elementos, de la misma matriz. Los arreglos son muy útiles en C y
Ejemplo:
19
Machine Translated by Google
Se accede a los elementos de una matriz con el primer elemento que tiene un
índice de 0. A estos elementos se accede de la siguiente manera:
Punteros
Un puntero es solo otra variable. Piense en punteros como int, char o float.
Sabemos que un int almacena números, char almacena caracteres individuales
y float almacena números decimales. Sin embargo, no dejes que el asterisco y
el ampersand te asusten.
Un puntero es simplemente una variable que almacena la dirección de
memoria de otra variable. Es bastante simple de entender. El listado 2-7 muestra
un ejemplo de cómo declarar un puntero y una asignación común.
// un puntero a un entero
int *numero_puntero;
20
Machine Translated by Google
¿Ves lo simples que son los punteros para usar? Si aún tiene
dificultades para entenderlos, investigue un poco más. Hay libros enteros
dedicados al uso de punteros.
Estructuras
Velocidad de estructura{
byte lento;
byte normal;
byte rápido;
};
Para usar una estructura, declara un tipo de estructura (vea el Listado 2-9).
Luego accede a un miembro de una estructura con un punto (.) como el miembro
operador de acceso de la siguiente manera:
MotorSpeed.slow = 10;
21
Machine Translated by Google
Operadores
// Comprueba la igualdad
X == Y;
22
Machine Translated by Google
// Operador lógico Y
X && Y;
// Operador OR lógico
X || Y;
23
Machine Translated by Google
if (velocidad ==
200) { turnLightOn(); }
otra declaración
if(velocidad ==
200)
{ enciendeLuz(); }
demás
{ mantenerLuzApagada(); }
if(velocidad ==
200)
{ enciendeLuzRoja(); }
24
Machine Translated by Google
demás
declaración de cambio
La sentencia switch se usa cuando necesitamos comparar una variable con diferentes
valores (vea el Listado 2-16). Se utiliza en situaciones en las que se habrían utilizado
demasiadas declaraciones if y else if. Debes recordar
cambiar (velocidad)
{
caso 100:
bipUnaVez();
descanso;
caso 150:
bipDosVeces();
descanso;
caso 200:
caso 250:
apagarMotor();
descanso;
25
Machine Translated by Google
descanso:
(); }
en bucle
El ciclo for se usa cuando necesita ejecutar una secuencia de sentencias varias
veces (vea el Listado 2-17).
retraso_ms(1000); }
mientras bucle
while(1)
{ leerSensor();
verificarbateria();
actualizarPantalla(); }
26
Machine Translated by Google
hacer bucle
El bucle do funciona igual que el bucle while, excepto que comprueba las
condiciones del bucle después de la ejecución y se ejecutará al menos una vez.
Consulte el Listado 2-19.
declaración de ruptura
continuar declaración
La instrucción continuar hace que se salte el resto de la iteración actual del ciclo
(vea el Listado 2-21).
27
Machine Translated by Google
{
Seguir;
}
spi_send(0x02);
}
ir a Declaración
La sentencia goto es vista con vergüenza. Sin embargo, hay casos
en los que un salto incondicional es útil. Aunque usando el goto
declaración a menudo conduce a un código de espagueti, es útil comprender
cómo funciona.
La declaración goto simplemente transfiere el flujo del programa a un
punto en el programa que tiene una etiqueta (vea el Listado 2-22). La única
vez que puede ser necesario usar un bucle goto es cuando hay bucles for
profundamente anidados o sentencias if.
miEtiqueta:
encenderAlgo();
ir a miEtiqueta;
28
Machine Translated by Google
editor de código donde escribes tu código, un depurador que te ayuda a buscar errores
compila este código, se convierte en algo llamado archivo de objeto. Después de este paso,
convierte en el archivo final que será ejecutado por su microcontrolador. Puede haber otros
pasos en este proceso de generar el archivo hexadecimal final (programa que se escribirá en
El preprocesador es otra parte del IDE que usa directivas, lo que hace que el programa C
#definir
La directiva #define es la primera que veremos. La instrucción #define en
C define macros. Esta declaración se usa mucho en la programación integrada y es muy útil.
En lugar de tener que seguir escribiendo alguna constante, es más fácil usar la directiva
#define. Esto también es útil en casos en los que
Por ejemplo, si estamos escribiendo un controlador para una pantalla LCD que viene en dos
constantemente esos números, ya que las dimensiones de la pantalla LCD seguirían siendo
29
Machine Translated by Google
30
Machine Translated by Google
#terminara si
Tenga en cuenta que las directivas condicionales deben terminar con un #endif
declaración.
#pragma
Esta es una directiva de C que en la programación de propósito general se usa para
Ensamblaje contra C
Hay personas que piensan que Assembly es mejor para el diseño de microcontroladores
de 8 bits. Este puede haber sido el caso hace varios años, pero ahora que los
escrito a mano es menos importante ahora que antes. El único caso en el que puede usar
Assembly es si necesita generar código eficiente en la versión gratuita del compilador XC8,
tiene un chip en un diseño heredado que solo puede usar Assembly o, por supuesto, desea
31
Machine Translated by Google
Conclusión
Este capítulo contiene una descripción general básica del lenguaje de programación C.
Con solo los conceptos presentados aquí, puede hacer mucho, ya que cubrimos
las palabras clave más importantes para nuestros propósitos. Sin embargo, el
simple hecho de conocer las palabras clave de un idioma no ayuda a dominarlo.
Se necesita práctica. Si no domina el lenguaje C, lo animo a buscar libros y
recursos de Internet para ayudarlo en su viaje con el lenguaje de programación C.
Si eres completamente nuevo en la programación en general, te recomiendo que
aprendas los conceptos básicos. El libro que personalmente recomiendo es
Beginning C, 5th Edition de Ivor Horton, disponible en Apress®. También hay
muchos recursos gratuitos en la web que enseñan conceptos completos de
programación para principiantes.
32
Machine Translated by Google
CAPÍTULO 3
Electrónica Básica
para Embebidos
Sistemas
Electrónica
La diferencia entre los diseñadores de sistemas integrados y los ingenieros
de software o técnicos de TI es el conocimiento profundo del hardware que poseen
los diseñadores integrados. Los diseñadores integrados deben tener conocimientos
de electrónica para diseñar sistemas integrados de manera eficaz. Debemos recordar,
por encima de todo, que las computadoras son simplemente dispositivos electrónicos
complejos y los microcontroladores son simplemente computadoras en miniatura.
Las resistencias, los capacitores, los diodos y los transistores son algunos de los
componentes básicos del hardware de la computadora. Para comprender estos
dispositivos más complejos, es importante comprender los componentes electrónicos
básicos a partir de los cuales se construyen estos dispositivos.
resistencias
Una resistencia se usa en electrónica para imponer resistencia en los circuitos. Las
resistencias se clasifican por la cantidad de ohmios, que es esencialmente una
medida de la cantidad de resistencia que proporcionan.
33
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_3
Machine Translated by Google
Los resistores se utilizan para reducir el flujo de corriente en un circuito y para reducir
34
Machine Translated by Google
La mayoría de las resistencias tienen cuatro bandas. Las dos primeras bandas son las más
dígitos significantes. La tercera banda te dice la potencia de 10 por la que tienes que multiplicar
sin embargo, para algunas aplicaciones, la tolerancia debe estar dentro de un rango muy
estrecho.
Las resistencias de montaje en superficie suelen utilizar las marcas de tipo E24 o
E96. El E24 tiene tres números. Los primeros dos números son los dígitos significativos y
el tercero es el índice de base 10 para multiplicar. Por ejemplo, una resistencia marcada con
La Figura 3-2 muestra el símbolo esquemático de la resistencia y la Figura 3-3 muestra una
resistencia real.
35
Machine Translated by Google
Potenciómetro
Un potenciómetro es un componente electrónico utilizado para variar la
cantidad de resistencia en un circuito. El potenciómetro también se conoce
como "pot" y contiene tres terminales. Una olla no es más que un divisor de
voltaje que el usuario puede ajustar. Un reóstato es otro dispositivo que puede
encontrar, y es simplemente una resistencia ajustable. El joystick de dos ejes
que se encuentra comúnmente en los controladores de juegos y los botones
de ajuste de volumen son aplicaciones comunes de los potenciómetros en el
mundo real. La Figura 3-4 muestra el símbolo esquemático del potenciómetro y la Figura 3-5 muestra un
36
Machine Translated by Google
Potenciómetro digital
Un potenciómetro digital o digipot es una versión digital de un potenciómetro.
Electrónicamente hablando, realiza las mismas funciones que un potenciómetro.
La ventaja del digipot es que los microcontroladores pueden ajustar su
resistencia utilizando algún protocolo de interfaz digital, como SPI o I2C,
mediante software.
37
Machine Translated by Google
PCM 4131
CS VDD
SCK POB
IDE prisionero de guerra
POA de VSS
fotorresistencia
Una fotorresistencia, también conocida como resistencia dependiente
de la luz (LDR) o fotocélula, es un tipo de resistencia en la que la resistencia
cambia con la intensidad de la luz. La Figura 3-8 muestra el símbolo
esquemático del fotorresistor y la Figura 3-9 muestra un fotorresistor real.
38
Machine Translated by Google
Condensador
Los capacitores se utilizan para almacenar energía eléctrica en un circuito
electrónico (vea la figura 3-12). Los condensadores vienen en paquetes axiales, radiales y SMT.
Consisten en dos placas de metal separadas por un aislante, llamado
dieléctrico. Este material dieléctrico está hecho de muchos materiales, incluidos
papel, cerámica, plástico e incluso aire. La capacitancia se mide en faradios (F),
aunque los microfaradios y picofaradios son las unidades de medida más utilizadas
en el uso diario.
El tipo de dieléctrico influye en las propiedades del condensador y
determina si el condensador está polarizado o no polarizado. Figura 3-10
muestra los símbolos esquemáticos del gorro polarizado y la Figura 3-11 muestra
los símbolos esquemáticos del gorro no polarizado.
El condensador más comúnmente encontrado es el condensador
electrolítico. Esto se debe a que almacenan una capacitancia relativamente grande
en relación con su tamaño. Están polarizados y se debe tener cuidado de no
conectarlos al revés. Vienen en dos variedades: tantalio y aluminio.
Los capacitores de aluminio son fácilmente reconocibles ya que generalmente
vienen en latas cilíndricas. Los capacitores de tantalio tienen una mayor relación
entre capacitancia y peso que los capacitores de aluminio y, por lo general, son más caros.
39
Machine Translated by Google
40
Machine Translated by Google
Inductor
Los inductores se utilizan para resistir los cambios en la corriente eléctrica que
fluye a través de ellos (vea la figura 3-14). El uso más común de los inductores es
en filtros, ya que un inductor deja pasar señales de baja frecuencia y resiste las de alta frecuencia.
El Henry (H) se utiliza para medir la inductancia. El nanohenrio, el
microhenrio y el milihenrio son las unidades más comunes.
La Figura 3-13 muestra los símbolos esquemáticos del inductor.
41
Machine Translated by Google
Transformadores
Un transformador es un dispositivo utilizado para aumentar o reducir
voltajes en dispositivos electrónicos.
42
Machine Translated by Google
Diodo
Un diodo es un dispositivo utilizado para permitir que la corriente fluya en una dirección particular.
Cuando el diodo tiene polarización directa, la corriente puede fluir. Cuando el diodo
43
Machine Translated by Google
dispositivos, ya que son imprescindibles para suprimir los picos de voltaje que
pueden estar presentes cuando se conducen cargas inductivas. La figura 3-17
muestra el símbolo esquemático del diodo.
Diodo Zener
Los diodos Zener son dispositivos que funcionan en la región de tensión de ruptura
y se utilizan para la estabilización y regulación de tensión y como referencia de tensión.
La figura 3-18 muestra el símbolo esquemático del diodo Zener. En la Figura 3-19, verá
el diodo Zener (cuerpo de vidrio) debajo de un diodo normal (cuerpo negro). Debe
tener en cuenta que los diodos normales también pueden tener cuerpos de vidrio.
44
Machine Translated by Google
45
Machine Translated by Google
Diodo láser
El diodo láser (consulte la figura 3-22) es otro tipo de diodo común en
los sistemas integrados. Son de bajo costo y pesan muy poco, lo que los
hace muy útiles para una variedad de proyectos.
46
Machine Translated by Google
transistores
Los transistores son posiblemente los dispositivos más revolucionarios jamás inventados.
Los dispositivos modernos no serían posibles sin el transistor. Los transistores se
utilizan principalmente para conmutación y rectificación en circuitos electrónicos.
Los transistores vienen en una variedad de tipos, que se discutirán brevemente en las
siguientes secciones.
47
Machine Translated by Google
48
Machine Translated by Google
Transistor Darlington
Un transistor Darlington consta de dos transistores conectados de tal manera
que la salida de corriente del primer transistor es amplificada aún más por el
segundo. El par Darlington usa dos transistores PNP o dos NPN y un Darlington
complementario usa un transistor NPN y otro PNP.
Actúan como un solo transistor con una alta ganancia de corriente. Esta
propiedad es importante en las aplicaciones integradas, ya que en los circuitos
basados en microcontroladores, pueden usar una pequeña cantidad de corriente
del microcontrolador para ejecutar una carga mayor. Esto les da muchos usos,
como controladores de pantalla y control de motores y solenoides.
El Photodarlington tiene una alta ganancia pero es más lento que los
fototransistores ordinarios.
49
Machine Translated by Google
50
Machine Translated by Google
Transistores (MOSFET)
tienen una gran ventaja sobre los BJT, ya que requieren menos voltaje para encenderse.
Así, mientras que los transistores son dispositivos basados en corriente, los
Los MOSFET deben manejarse con cuidado, ya que se dañan fácilmente con
electricidad estática. La figura 3-27 muestra el símbolo esquemático MOSFET.
51
Machine Translated by Google
amplificación y como resistencia controlada por voltaje. Los JFET no se usan comúnmente en el
El JFET también encuentra uso como un interruptor controlado por voltaje y como un interruptor.
52
Machine Translated by Google
Amplificador operacional
El amplificador operacional u op-amp es uno de los componentes básicos de la
electrónica analógica. Incluso iría tan lejos como para decir que un amplificador
muy potentes. La figura 3-29 muestra el símbolo esquemático del amplificador operacional.
Como su nombre lo indica, el amplificador operacional se utiliza para la amplificación de señales de CC.
También se utiliza para filtrar y acondicionar señales, así como para integración,
diferenciación, resta y suma.
pines de suministro (que generalmente se omiten), verá dos terminales, uno con el
signo menos y el otro con el signo positivo. La entrada con el signo positivo se conoce
53
Machine Translated by Google
El signo menos se llama entrada inversora. Hay un tercer pin en el vértice del símbolo del
amplificador operacional de forma triangular, conocido como puerto de salida, y este pin
puede permitir que el voltaje o la corriente fluyan hacia el dispositivo, lo que se denomina
filtros activos de paso bajo y paso alto, amplificación de fotodiodos y más. De hecho, ¡se
puede escribir un libro completo sobre los amplificadores operacionales y sus aplicaciones!
son excelentes para la creación rápida de prototipos. Una vez que tenga un sistema en
Electrónica digital
Las puertas lógicas son los componentes básicos de los circuitos digitales. Cuando
combina varios transistores, obtiene puertas lógicas. Le dejo a usted leer sobre las
siguientes secciones.
La puerta Y
La puerta AND es uno de los componentes básicos de la lógica digital.
La puerta AND funciona tratando dos entradas y salidas lógicas como un nivel lógico alto
solo si ambas entradas son altas. Si solo una de las entradas es alta, entonces la salida
será lógicamente baja. La figura 3-30 muestra el símbolo esquemático de la puerta AND.
54
Machine Translated by Google
La puerta OR
La puerta OR funciona emitiendo un alto lógico si cualquiera de sus entradas es
un alto lógico. Si ambas entradas son un nivel lógico alto, entonces la salida
también es un nivel lógico alto. La única vez que la puerta OR genera un nivel bajo
lógico es si ambas entradas son un nivel bajo lógico. La figura 3-31 muestra el símbolo esquemático de la p
La puerta NO
La puerta NOT, también conocida como puerta de inversión, produce
exactamente lo contrario de su entrada. Si la entrada es un alto lógico, entonces la
salida será un bajo lógico, y si la entrada es un bajo lógico, entonces la salida será un
alto lógico. La figura 3-32 muestra el símbolo esquemático de la puerta NOT.
55
Machine Translated by Google
La puerta NAND
La puerta NOT AND (NAND) es una puerta lógica que combina una puerta NOT y
una puerta AND. La única característica distintiva entre la puerta NAND y la puerta
AND es el pequeño círculo en el extremo que simboliza la inversión.
La puerta NAND solo da un bajo lógico si ambas entradas son altos lógicos.
La figura 3-33 muestra el símbolo esquemático de la puerta NAND.
La puerta NOR
La puerta NOT OR (NOR) es una puerta lógica que combina una puerta NOT y una
puerta OR. La puerta NOR, como la puerta NAND, simplemente invierte la salida de
una puerta OR y tiene la misma característica distintiva. La figura 3-34 muestra el
símbolo esquemático de la puerta NOR.
56
Machine Translated by Google
La puerta de amortiguamiento
La puerta del búfer es simplemente dos puertas NO combinadas. La puerta del búfer
puede parecer inútil, pero en realidad tiene muchas aplicaciones con conversión de nivel
La puerta XOR
La compuerta OR exclusiva (XOR) es una compuerta lógica que proporciona un valor
lógico bajo cuando ambas entradas son verdaderas o cuando ambas entradas son
falsas. Da un alto lógico cuando ambas entradas son lógicamente opuestas. La figura
57
Machine Translated by Google
58
Machine Translated by Google
Para evitar esto, existen formas comunes de encubrir un sistema de nivel lógico de 5v
para ser interconectado con un sistema de nivel lógico de 3.3 voltios, discutido a continuación.
59
Machine Translated by Google
60
Machine Translated by Google
Conclusión
Este capítulo analizó los componentes electrónicos básicos que se
encuentran comúnmente en los sistemas integrados. Cubrimos varios
componentes, así como puertas lógicas básicas y métodos de conversión de
nivel lógico. Este capítulo es esencial para comprender cómo conectar dispositivos y sensores a
Fue una introducción muy básica; sin embargo, si comprende el contenido
aquí, debería poder construir sus propios circuitos. Si necesita más
información, hay libros disponibles que dan una descripción más detallada de
los componentes. También existe una aplicación llamada Logic Gates para
dispositivos Android que permite a los usuarios experimentar con compuertas lógicas.
61
Machine Translated by Google
CAPÍTULO 4
Microcontroladores PIC®
Descripción general de los microcontroladores PIC®
las familias de 8 bits. Los microcontroladores PIC® de 8 bits pertenecen a diferentes grupos
según los clasifica el microchip. Los grupos son línea de base, rango medio, rango medio
63
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_4
Machine Translated by Google
64
Machine Translated by Google
Programa
Memoria flash
RAM PORTA
PUERTOB
CLKOUT tiempo
Generacion
CLKIN HFINTOSC/
PUERTO
LFINTOGC
Oscilador
UPC
MCLR
suboficial
ZCD
amplificadores operacionales
PWM Temporizador0 Temporizador1 Temporizador2 MSSP Comparadores
DIENTE
Temperatura. ADC
RVF DAC PCC EUSART CLC
Indicador 10 bits
sesenta y cinco
Machine Translated by Google
PIC16F1717 es muy complejo. Consta de una gran cantidad de periféricos, de los que
hablaremos en las próximas secciones.
hecha con tecnología de memoria Flash. Los chips más antiguos requerían luz ultravioleta
para borrar su memoria. Sin embargo, con la llegada de la tecnología basada en Flash, los
segundos. Flash también es una forma de memoria no volátil y presenta una retención de datos
prolongada. ¡La memoria flash PIC16F1717 tiene una retención de datos de aproximadamente
40 años!
puede almacenar bastantes instrucciones, como verá en este libro. Los sistemas de
programa.
para aumentar la velocidad de ejecución del programa. Hay dos tipos principales de RAM: RAM
estática (SRAM) y RAM dinámica (DRAM). Hay otros tipos de RAM, como FRAM y EERAM; sin
utiliza para la memoria. Sin embargo, en los microcontroladores, encuentra SRAM que se
utiliza para la memoria principal. Las CPU de uso general contienen SRAM; sin embargo,
66
Machine Translated by Google
memoria. Las principales diferencias son que SRAM retiene sus datos si se aplica
energía, mientras que DRAM necesita una actualización constante. SRAM también
es más rápido que DRAM.
Generación de tiempos
Si observa el diagrama de bloques en la Figura 4-1, verá un bloque titulado
"Generación de temporización". Este bloque contiene HFINTOSC y LFINTOSC, que
son el oscilador interno de alta frecuencia y el oscilador interno de baja frecuencia,
respectivamente. Además, no mencionado aquí, es el MFINTOSC (oscilador interno
de frecuencia media). El LFINTOSC funciona a 31 kHz y no está calibrado. El
MFINTOSC funciona a 500 kHz y viene calibrado de fábrica. El HFINTOSC deriva su
velocidad del MFINTOSC y funciona a una velocidad de hasta 16 MHz.
67
Machine Translated by Google
!MCLR
El pin !MCLR se utiliza para restablecer el microcontrolador PIC®. Cuando diseñe
circuitos, no deje este pin flotando. Este pin debe estar conectado a VDD si no
está en uso. El circuito utilizado para el !MCLR se muestra a continuación.
Puertos
Periféricos a bordo
El microcontrolador PIC® consta de varios periféricos digitales. Estos
periféricos son de naturaleza digital o analógica. Microchip introdujo
recientemente una gran cantidad de periféricos independientes del núcleo.
Los periféricos independientes del núcleo no requieren la intervención de la CPU para mantener el funci
Veamos estos periféricos.
68
Machine Translated by Google
una resolución de 4mV por bit. Para una lectura precisa de ADC, se recomienda tener una
DAC convierte una señal analógica en una digital. El DAC se usa típicamente para
generar sonido y formas de onda. El PIC16F1717 tiene dos DAC. DAC1 tiene una resolución
de 8 bits y DAC2 tiene una resolución de 5 bits.
onda cuadrada de frecuencia y ciclo de trabajo variables, que el usuario puede determinar.
En la práctica, he encontrado que uno tiende a usar PWM con más frecuencia debido a su
módulos CCP regulares. El módulo PWM del PIC16F1717 tiene una resolución de 10 bits.
69
Machine Translated by Google
Temporizadores
tiempo, generar pulsos y contar pulsos, y también son retrasos de tiempo muy precisos. Por
Comparadores
El comparador del PIC16F1717 compara dos voltajes y proporciona una salida digital para
indicar cuál es mayor. El comparador tiene una histéresis mínima de 20mV y máxima de 75mV.
estable al comparador, DAC o ADC. Al hacer esto, se elimina el costo de pagar una referencia
de voltaje externa.
Indicador de temperatura
Hay un indicador de temperatura a bordo del PIC16F1717 que tiene un rango de -40 a 85 grados
Celsius. Este indicador de temperatura es útil cuando no tiene espacio en la placa para un
70
Machine Translated by Google
EUSART
El módulo Enhanced Universal Synchronous Asynchronous Receiver Transmitter
(EUSART) se utiliza para comunicaciones en serie y muchos módulos externos
requieren esta interfaz para comunicarse con el microcontrolador.
CVX
La celda lógica configurable (CLC) es un módulo en el microcontrolador que
proporciona un poco de funciones lógicas secuenciales y combinatorias integradas.
MSSP
El módulo de puerto serie síncrono maestro (MSSP) proporciona dos modos de
funcionamiento que se configurarán para su uso, ya sea para la función de interfaz
periférica serie (SPI) o las funciones de circuito interintegrado (I2C).
suboficial
ZCD
El módulo de detección de cruce por cero detecta el punto cuando no hay
voltaje presente en la forma de onda de CA. El módulo ZCD se puede utilizar
para detectar la frecuencia fundamental de una forma de onda y para enviar
datos digitales a través de circuitos de CA, como se hace en los sistemas X10.
La detección de cruce por cero en el PIC16F1717 tiene un tiempo de respuesta de 1uS.
71
Machine Translated by Google
DIENTE
Amplificadores operacionales
Los amplificadores operacionales (OPA) o los amplificadores operacionales son la piedra angular de
producto de ancho de banda de ganancia de 2 MHz y una velocidad de respuesta de 3 V por EE. UU.,
suponiendo un VDD de 3,0 V.
72
Machine Translated by Google
15
Configuración 15 8
Bus de datos
Contador de programa
Destello
Programa
Memoria Stock de 16 niveles
RAM
(15 bits)
Leer (PMR)
Dirección MUX
Registro de instrucción
Indirecto
Dirección directa 7
5 Dirección 12 12
15 Reg. BSR
Registro FSR0
Registro FSR1
15
ESTADO Reg.
8
3
multiplexor
Encender
Temporizador
Instrucción Oscilador
Decodificar y Temporizador de puesta en marcha
ALU
Control
Encendido
OSC1/CLKIN 8
Reiniciar
Apagón
Reiniciar
Interno
Oscilador
Cuadra
vco vs
73
Machine Translated by Google
Temporizador de encendido
que la fuente de alimentación alcance el valor requerido. Una vez transcurrido el tiempo, el programa
puede comenzar a ejecutarse. La razón de esto es que es una precaución para evitar efectos
El temporizador de inicio del oscilador (OST) proporciona un retraso (además del que ofrece el
temporizador de encendido) para permitir que el reloj se estabilice antes de que comience la
ejecución del programa. El OST cuenta por un período de 1024 ciclos y es independiente de la
Restablecimiento de encendido
Temporizador de vigilancia
un período determinado definido por el usuario. Esto es extremadamente importante para permitir
que una aplicación escape de un bucle sin fin. Para mantener el programa
74
Machine Translated by Google
Restablecimiento de Brown-Out
El restablecimiento de caída de tensión se utiliza para detectar una condición de caída de tensión dentro del
Conclusión
En este capítulo, examinamos brevemente el microcontrolador PIC®, que es el
tema principal de este libro. Aprenda la información presentada en este capítulo y
aprenda bien. El capítulo cubrió algunos de los periféricos más importantes a bordo del
microcontrolador y proporcionó una descripción general de algunas de las características
del núcleo.
75
Machine Translated by Google
CAPÍTULO 5
Conectando
y Creando
Empecemos
En este capítulo, analizamos el proceso de conexión de un microcontrolador
PIC® a un programador en serie en circuito, o ICSP. Algunas personas también
los llaman Depuradores en circuito, ya que también tienen capacidades de
depuración. Observamos el proceso de creación y ejecución de un nuevo
proyecto en MPLAB X, así como la creación de archivos fuente. Los principiantes
y los usuarios primerizos de microcontroladores básicos deben prestar especial
atención a este capítulo porque, a diferencia de Arduino y otras placas de
desarrollo, es necesario conectar el microcontrolador a un programador para
cargar su programa en el chip. El proceso no es tan "plug and play" como usar una placa de desar
77
© Armstrong Subero
2018 A. Subero, Programación de Microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_5
Machine Translated by Google
78
Machine Translated by Google
79
Machine Translated by Google
81
Machine Translated by Google
82
Machine Translated by Google
Seleccione un nombre para su proyecto y elija una ruta. Luego haga clic en el
botón Finalizar. Esto se representa en la Figura 5-11. ¡Felicidades! ¡Has creado un nuevo
proyecto!
83
Machine Translated by Google
84
Machine Translated by Google
Ahora está listo para escribir su programa. Para hacer esto, debe crear archivos de
Haga clic en la carpeta Archivos de origen. A continuación, haga clic con el botón derecho en él y seleccione Nuevo.
en lugar de hacer clic en la carpeta Archivos de origen, hace clic en la carpeta Archivos de
encabezado y crea un archivo de encabezado en lugar de un archivo de origen.
Mire la parte superior del IDE y verá los dos íconos que usaremos. los
usa para ejecutar el proyecto principal (consulte la Figura 5-14). Usamos el botón Clean and
Build para verificar que nuestro programa está libre de errores. Al hacer clic en el icono
85
Machine Translated by Google
La otra trampa que debe tener en cuenta es la longitud de los cables desde
el programador al chip. Mantenga estos cables lo más cortos posible, lo que
garantizará que no experimente ningún error al intentar ejecutar su proyecto.
La trampa final con la que debe tener cuidado es tener una fuente de
alimentación ruidosa. Le recomiendo que use una fuente de alimentación dedicada,
ya que tendrá la menor cantidad de ruido. Incluso con una buena fuente de
alimentación, le recomiendo que siga usando algunos condensadores de suavizado
en los rieles de suministro. Innumerables problemas evitables se pueden atribuir a tener una fuente de alimentac
Recuerde que programar un chip es un proceso complejo y muchas cosas suceden
en segundo plano. Por lo tanto, cualquier cosa puede salir mal. Es muy importante que
prestes atención a la ventana de salida. Te ahorrará muchas horas de frustración.
86
Machine Translated by Google
información adicional
Si necesita más información sobre cómo se lleva a cabo la programación real
del microcontrolador PIC, le recomiendo que consulte la nota de la aplicación
DS30277. Microchip también proporciona mucha información en su sitio web
sobre el uso de microcontroladores PIC en general en www.microchip.com. Hay
muchos recursos adicionales en el sitio web del fabricante.
Conclusión
Este capítulo analizó el proceso de conexión de nuestro microcontrolador
a nuestro programador. También analizamos los procesos de creación de un
nuevo archivo de origen del proyecto, así como también cómo construiría y ejecutaría un proyecto
87
Machine Translated by Google
CAPÍTULO 6
Entrada y salida
Comencemos E/S
En el último capítulo, analizamos los periféricos disponibles para los usuarios de PIC®
microcontroladores Una de las cosas que vimos en el diagrama de bloques
fueron los puertos. Como se describió, los puertos son registros que brindan
acceso a los pines del microcontrolador. Los puertos del PIC16F1717 se
pueden usar para entrada o salida y pueden brindar acceso a muchos
periféricos integrados en el microcontrolador. En este capítulo, analizamos la
entrada y la salida, también escritas como E/S. Usamos E/S para interactuar con
LED, interruptores y pantallas de siete segmentos.
Antes de ver el código para que esto suceda, hay algunos registros
que debe comprender, incluido saber cómo configurarlos para usar E/S de
manera efectiva en el microcontrolador.
Registro TRIS
El primer registro que examinamos es el registro TRIState (TRIS). El registro
TRIS recibe su nombre del hecho de que puede estar en tres estados. Se
puede configurar para salida alta, salida baja o entrada. El registro TRIS se
utiliza para hacer que un puerto sea una entrada o una salida. Para convertir un
puerto en una salida, escribimos un 0 en el registro TRIS correspondiente de
ese puerto. Para que sea una entrada, escribimos un 1.
89
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_6
Machine Translated by Google
Por ejemplo, para configurar PORTB como puerto de salida, hacemos lo siguiente:
TRIB = 0;
hacemos lo siguiente:
TRIB = 1;
TRISBbits.TRISB0 = 0;
TRISBbits.TRISB1 = 1;
Como puede ver en estas asignaciones, Microchip hace que sea muy fácil
acceder a las E/S individuales en los microcontroladores PIC® y configurar sus
registros asociados. No hay necesidad de meterse con funciones de puntero
esotéricas o lidiar con lógica bit a bit alucinante. De hecho, acceder a pines
individuales para operaciones de E/S en microcontroladores PIC® es más fácil en un
microcontrolador.
Es imperativo recordar que los datos no se moverán del registro del puerto a
los pines del microcontrolador a menos que le dé algún valor al registro TRIS apropiado.
90
Machine Translated by Google
PUERTO Registro
91
Machine Translated by Google
ese poco Sin embargo, si desea utilizar un pin de E/S en particular como pin de
entrada analógica, debe configurar el registro ANSEL correspondiente.
dominada débil
Los puertos del PIC16F1717 tienen resistencias pull-up internas. Estos son
importantes ya que reducen el número de componentes al eliminar la necesidad
de una resistencia externa. El pull-up débil puede usarse como se ve en el Listado 6-1.
OPTION_REGbits.nWPUEN = 0;
92
Machine Translated by Google
Sería bueno hacer esto solo, pero primero hay algunas cosas que debemos
hacer. Tan importante como es el software, el hardware es muy importante cuando se
diseña con microcontroladores. A veces, un programa se compilará y no se ejecutará
como se esperaba.
93
Machine Translated by Google
Figura 6-1.
94
Machine Translated by Google
/*
* Archivo: 16F1717_Internal.h
* Autor: Armstrong Subero
* FOTO: 16F1717 con X OSC @ 16MHz, 5v
*
Programa: archivo de encabezado para configurar PIC16F1717
***************/
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
95
Machine Translated by Google
// CONFIG1
en pin CLKIN)
(PWRT deshabilitado)
#pragma config CLKOUTEN = OFF // Habilitar salida de reloj (la función CLKOUT está
deshabilitada.
E/S o función de oscilador en el pin
CLKOUT)
Modo (Interno/Externo
96
Machine Translated by Google
// CONFIG2
SPLLEN)
pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable
(Stack\Overflow o Underflow provocará
un reinicio)
#pragma config BORV = LO // Selección de voltaje de restablecimiento de
apagón (Apagón\Voltaje de reinicio (Vbor),
punto de disparo bajo seleccionado).
#pragma config LPBOR = OFF // Restablecimiento de Brown Out de baja potencia
(El BOR de bajo consumo está deshabilitado)
97
Machine Translated by Google
//Otro Incluye
#incluir <stdint.h>
#incluir <stdbool.h>
#incluir <stddef.h>
#incluir <matemáticas.h>
98
Machine Translated by Google
/*
* Archivo: 16F1717_Internal.c
* Autor: Armstrong Subero
* FOTO: 16F1717 con OSC interno @ 16MHz, 5v
*
Programa: archivo de biblioteca para configurar PIC16F1717
* Compilador: XC8 (v1.35, MPLAX X v3.10)
*
Versión del programa: 1.2
** Se agregaron comentarios adicionales
*
*
Descripción del programa: esta biblioteca le permite configurar un
FOTO16F1717
*
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
99
Machine Translated by Google
/**************************************************** ************
****************
* Función: interno_32()
*
* Devoluciones: Nada
*
*
Descripción: Ajusta el oscilador interno a 32MHz
*
*
Uso: interno_32()
**************************************************** ************
***************/
//Establecer a
32MHz void internal_32(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
//CONFIGURAR PLLx4 EN
SPLLEN =
1; }
/**************************************************** ************
****************
* Función: interno_16()
*
* Devoluciones: Nada
*
100
Machine Translated by Google
*
Descripción: Ajusta el oscilador interno a 16 MHz
*
*
Uso: internal_16()
**************************************************** ************
***************/
//Establecer a 16MHz
vacío interno_16(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
SPLLEN =
0; }
/**************************************************** ************
****************
* Función: interno_8()
*
* Devoluciones: Nada
*
*
Descripción: Ajusta el oscilador interno a 8 MHz
*
*
Uso: interno_8()
**************************************************** ************
***************/
101
Machine Translated by Google
//Establecer a 8MHz
vacío interno_8(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
SPLLEN =
0; }
/**************************************************** ************
****************
* Función: interno_4()
*
* Devoluciones: Nada
*
*
Descripción: Ajusta el oscilador interno a 4MHz
*
*
Uso: interno_4()
**************************************************** ************
***************/
//Establecer a
4MHz void internal_4(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
102
Machine Translated by Google
SPLLEN =
0; }
/**************************************************** ************
****************
* Función: interno_2()
*
* Devoluciones: Nada
*
*
Descripción: Ajusta el oscilador interno a 2 MHz
*
*
Uso: interno_2()
**************************************************** ************
***************/
//Establecer a 2MHz
vacío interno_2(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
103
Machine Translated by Google
SPLLEN =
0; }
/**************************************************** ************
****************
* Función: interno_1()
*
* Devoluciones: Nada
*
*
Descripción: Ajusta el oscilador interno a 1 MHz
*
*
Uso: interno_1()
**************************************************** ************
***************/
//Establecer a 1MHz
vacío interno_1(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
SPLLEN =
0; }
104
Machine Translated by Google
/**************************************************** ************
****************
* Función: interno_31()
*
* Devoluciones: Nada
*
*
Descripción: Ajusta el oscilador interno a 31kHz
*
*
Uso: internal_31()
**************************************************** ************
***************/
//Establecer a
31kHz(LFINTOSC) void internal_31(){
//Reloj determinado por FOSC en bits de configuración
SCS0 = 0;
SCS1 = 0;
SPLLEN =
0; }
Ahora viene la parte buena: hacer que el LED se encienda. Todos los
programas en C, como saben, deben contener una función principal.
Tradicionalmente, el archivo que contiene esta función también se llama main. Por
lo tanto, cree un nuevo archivo llamado main.c e ingrese el código, como se muestra en el Listado 6-4.
105
Machine Translated by Google
/*
* Archivo: Principal.c
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
106
Machine Translated by Google
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
while(1){ //
Establecer PIND1
High LATDbits.LATD1
= 1; }
regreso;
107
Machine Translated by Google
cableado correctamente.
/*
* Archivo: Principal.c
9 *
*
Descripción del programa: este programa permite que PIC16F1717
parpadee un LED en
* 50% ciclo de trabajo
*
108
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
109
Machine Translated by Google
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
mientras(1){
// Alternar LED
LATDbits.LATD1 = ~LATDbits.LATD1;
// retraso 500ms
__retraso_ms(500);
}
regreso;
Uso de un pulsador
Ahora que examinamos la salida, veamos la entrada. Como se mencionó
anteriormente, el registro TRIS debe configurarse para permitir que el pin de E/S actúe como una entrada.
También usamos el pull-up interno para evitar el uso de una resistencia externa.
Como hicimos con la salida, también hay un proceso para convertir el pin en
un pin de entrada. Estos son los pasos para configurar un pin como entrada
usando las resistencias pull-up débiles internas en el chip:
111
Machine Translated by Google
El Listado 6-6 muestra el código principal. Los archivos de encabezado y los bits
de configuración son los mismos que en el último ejemplo.
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
112
Machine Translated by Google
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar ANSELB0
ANSELBbits.ANSB0 = 0;
113
Machine Translated by Google
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
mientras(1){
// Activar LED en pulsador
LATDbits.LATD1 = ~PORTBbits.RB0;
}
regreso;
Puede haber momentos en los que sea necesario eliminar el rebote de su interruptor.
El rebote es cuando permite que el microcontrolador solo reconozca
que el interruptor se presionó una vez, incluso cuando se presiona varias veces.
Algunos interruptores de muy mala calidad pueden hacer múltiples
contactos con solo presionar un botón. Esto, a su vez, hace que el
microcontrolador registre varias veces que se presiona el interruptor.
Hay varias opciones para eliminar rebotes, incluidos los métodos de
hardware y software. Sin embargo, descubrí que en la práctica, los botones
mecánicos de alta calidad que se utilizan para crear prototipos de
aplicaciones no necesariamente necesitan eliminar el rebote, especialmente
para aplicaciones tan triviales como la nuestra. Sin embargo, si está
diseñando un producto comercial, se recomienda encarecidamente que agregue antirrebote a sus inte
114
Machine Translated by Google
si (RB0 == 0)
{
// breve retraso
__retraso_ms(100);
demás{
LATDbits.LATD1 = 0;
}
115
Machine Translated by Google
116
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
117
Machine Translated by Google
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar ANSELB0
ANSELBbits.ANSB0 = 0;
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
118
Machine Translated by Google
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
mientras(1){
// Alternar LED en el botón PUSH
LATDbits.LATD1 = ~PORTBbits.RB0;
}
regreso;
119
Machine Translated by Google
Como ves, cada pin está asociado con una letra AG, y también hay un pin
para el punto decimal marcado como DP. Hay dos pines marcados COM.
Esta es la abreviatura de "común" y se conectará a tierra.
Para mostrar números en la pantalla, los segmentos asociados con ese pin
están activados. Por ejemplo, para mostrar el número 8, todos los segmentos del
LED estarían encendidos. Estos pines luego se conectarían a un puerto particular en
el microcontrolador. Luego, los números se enviarían al puerto del microcontrolador
al que está conectada la pantalla de siete segmentos.
Dado que la pantalla de siete segmentos es, después de todo, un grupo de
LED en un solo paquete, debe conectar resistencias a cada segmento de las
pantallas para asegurarse de no dañarlas.
Tenga en cuenta que si está utilizando la variedad de ánodo común, el hexadecimal
los números enviados al puerto serán ligeramente diferentes. Esto se debe a que
en la variedad de ánodo común, todos los LED están conectados a la alimentación
en lugar de a tierra.
120
Machine Translated by Google
Ahora que tiene una idea clara de cómo funcionan estas pantallas, vamos a
mire el esquema para conectar la pantalla de siete segmentos al microcontrolador
(vea la Figura 6-5). Los pines están conectados a PORTD, con A conectado a RD0, B
conectado a RD1, y así sucesivamente, hasta que todos los pines estén conectados con
la excepción del pin del punto decimal. El común está conectado a tierra.
Ahora escribimos el código requerido para usar una pantalla de siete segmentos,
como se muestra en el Listado 6-9.
121
Machine Translated by Google
/*
* Archivo: Principal.c
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
122
Machine Translated by Google
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
123
Machine Translated by Google
// variable de bucle
ent yo;
initPrincipal();
// Por ejemplo, para mostrar el número '7', esto significa que debemos tener
los segmentos // a, b y c habilitados. Esto sería "0000111" en binario con
un '1' que significa // el segmento en particular. Esto equivaldría a 0x07 en
hexadecimal. Así que cuando
LATD = Pantalla(i);
__retraso_ms(1000);
}
regreso;
124
Machine Translated by Google
/**************************************************** ************
****************
***************/
{
// variable que representa numeros
números de caracteres sin firmar;
// devolverlo
números de retorno;
}
125
Machine Translated by Google
casi la mitad de los pines del PIC16F1717. Para evitar esto, puede usar un
microcontrolador con mayor número de pines, que cuesta más y aumenta el
costo total de su sistema, o puede usar multiplexación.
126
Machine Translated by Google
/*
* Archivo: Principal.c
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
127
Machine Translated by Google
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
128
Machine Translated by Google
// Activar analógico en C
ANSELC = 0;
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
// variable de conteo
conteo int = 0;
initPrincipal();
129
Machine Translated by Google
si (RC4 == 0)
{ contar++; } }
if(RC5 == 0)
{ contar--; } }
MSD = recuento/10;
LSD = recuento% 10;
// Mostrar MSD
LATD = Pantalla (MSD);
DIGITO DOS = 1;
__retraso_ms(20);
DIGITO DOS = 0;
130
Machine Translated by Google
// Mostrar LSD
LATD = Pantalla (LSD);
DIGITONO = 1;
__retraso_ms(20);
DIGITONO = 0;
// Si el valor no es válido, se
establece en 0 si (cuenta > 99 ||
cuenta < 0){ cuenta = 0; }
__retraso_ms(1); }
regreso;
/**************************************************** ************
****************
**************************************************** ************
***************/
131
Machine Translated by Google
// devolverlo
números de retorno;
}
132
Machine Translated by Google
Figura 6-7. Esquema del proyecto del temporizador de cuenta regresiva PIC16F1717
133
Machine Translated by Google
/*
* Archivo: Principal.c
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
134
Machine Translated by Google
#incluye "16F1717_Internal.h"
// puerto de
visualización #define DISPLAYPORT LATD
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
135
Machine Translated by Google
// Activar analógico en C
ANSELC = 0;
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
136
Machine Translated by Google
// variable de conteo
initPrincipal();
mientras(1){
if(RC5 == 0)
{ contar--; } }
regresiva (cuenta); } }
137
Machine Translated by Google
// Si el valor no es válido, se
establece en 0 si (cuenta > 99 ||
cuenta < 0){ cuenta = 0; }
// mostrar el número en
pantalla showNumber(count);
__retraso_ms(1); }
regreso;
/**************************************************** ************
****************
**************************************************** ************
***************/
138
Machine Translated by Google
// devolverlo
números de
retorno; }
/**************************************************** ************
****************
* Devoluciones: nada
*
*
Descripción: comienza una cuenta regresiva basada en el número
pasado a la función
**************************************************** ************
***************/
// comienza la cuenta
atrás para (i = número; i >= 0; i--)
{ showNumber(i);
__retraso_ms(1000); }
regreso;
139
Machine Translated by Google
/**************************************************** ************
****************
* Devoluciones: nada
*
*
Descripción: Muestra un número en el puerto especificado
**************************************************** ************
***************/
MSD = número/10;
LSD = número% 10;
// Mostrar MSD
DISPLAYPORT = pantalla (MSD);
DIGITO DOS =
1; __retraso_ms(20);
DIGITO DOS = 0;
// Mostrar LSD
DISPLAYPORT = pantalla (LSD);
DIGITONO = 1;
__retraso_ms(20);
DIGITONO = 0;
140
Machine Translated by Google
Si hubiéramos cubierto las interrupciones y los temporizadores, este proyecto habría sido
mucho más eficiente y eficaz. Actualmente, durante la cuenta regresiva, las pantallas
parpadean cada segundo. Esto se debe a que, como estamos usando la función de
retardo, durante un segundo el microcontrolador no hace nada. Cuando aprenda sobre
los temporizadores en el Capítulo 8, puede volver a visitar este proyecto y usar uno de
los módulos de temporizador integrados si lo desea.
141
Machine Translated by Google
Conclusión
Este capítulo analizó cómo usar la entrada y salida en un PIC®
microcontrolador mientras observa sus aplicaciones de conducción de LED,
interruptores y pantallas de siete segmentos. Cubrimos la multiplexación de
pantallas y vio cómo crear un proyecto simple utilizando lo que ha aprendido hasta ahora.
142
Machine Translated by Google
CAPÍTULO 7
Actuadores de interfaz
Introducción a los actuadores
Hasta ahora, hemos cubierto la entrada y salida de los microcontroladores PIC®.
En este capítulo, aplicamos el conocimiento que aprendimos hasta ahora
para controlar tres actuadores comunes en sistemas integrados: el motor
de CC, el motor paso a paso y el servomotor. Examinamos de forma
incremental estos tres actuadores en función de su capacidad para ser
controlados. Comenzamos con el motor de CC, que tiene un control de
direccionalidad muy aproximado (hacia adelante o hacia atrás) y es el más
fácil de controlar. A continuación, observamos el servo, que tiene un mayor
grado de capacidad de control, ya que se puede colocar en uno de tres ángulos.
Finalmente, el motor paso a paso tiene el nivel más fino de granularidad y el control se reduce a un
143
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_7
Machine Translated by Google
144
Machine Translated by Google
145
Machine Translated by Google
/*
* Archivo: Principal.c
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
146
Machine Translated by Google
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
}
/**************************************************** ************
*******
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
******/
vacío principal
(vacío) { initMain ();
regreso;
}
147
Machine Translated by Google
Esto debería ser suficiente para impulsar motores pequeños para aplicaciones triviales.
Al igual que en el programa LED, esta línea eleva el pin durante un período de cinco segundos,
Cuando el bucle while se ejecuta de nuevo, el pin tiene una salida baja, lo que apaga el
transistor durante un período de cinco segundos. Verá que incluso una aplicación simple,
como hacer parpadear un LED, se puede modificar para realizar otras tareas en el mundo real.
segundos lo determina el usuario y usted puede modificarlo como mejor le parezca. Si desea
que el motor gire en la otra dirección, puede hacerlo modificando el circuito y simplemente
cambiando las terminales del motor. Para hacerlo, conecte el terminal positivo a tierra y conecte
cubriremos la modulación de ancho de pulso (PWM). En ese mismo capítulo, veremos cómo
Servo motor
Ahora que tiene una comprensión justa del motor de CC, veamos el servomotor. Hay
muchos tipos y tamaños diferentes de servos. Hay servos de CA que se usan normalmente
más pequeñas. De hecho, hay servos que se usan exclusivamente para robots y se conocen
148
Machine Translated by Google
149
Machine Translated by Google
/*
* Archivo: Servo.h
* Autor: Armstrong Subero
* FOTO: 16F1717 con OSC interno @ 16MHz, 5v
*
Programa: Archivo de cabecera para controlar un servo
estándar * Compilador: XC8 (v1.38, MPLAX X v3.40)
150
Machine Translated by Google
*
Versión del programa 1.0
*
*
Descripción del programa: este encabezado de programa le permitirá
controlar un estándar
* servo
*
*
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/*
* Archivo: Servo.c
* Autor: Armstrong Subero
* FOTO: 16F1717 con OSC interno @ 16MHz, 5v
*
Programa: archivo de biblioteca para configurar PIC16F1717
* Compilador: XC8 (v1.38, MPLAX X v3.40)
*
Versión del programa: 1.0
*
151
Machine Translated by Google
*
Descripción del programa: esta biblioteca le permite controlar un
servo estándar
*
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Función: void servoRotate0()
*
* Devoluciones: Nada
*
*
Descripción: Gira el servo a la posición de 0 grados
*
**************************************************** ************
****************/
void servoRotate0()
{ unsigned int i;
para(i=0;i<50;i++) {
RD0 =
1; __delay_us(700);
RD0 =
0;
__delay_us(19300); } }
152
Machine Translated by Google
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Gira el servo a la posición de 90 grados
*
**************************************************** ************
****************/
RD0 =
1; __delay_us(1700);
RD0 =
0;
__delay_us(18300); } }
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Gira el servo a la posición de 180 grados
*
**************************************************** ************
****************/
153
Machine Translated by Google
RD0 = 1;
__delay_us(2600);
RD0 = 0;
__delay_us(17400); }
/*
* Archivo: Principal.c
154
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
#incluye "Servo.h"
**************************************************** ************
****************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/////////////////////
// Configurar
puertos ///////////////////
// Hacer salida D0
TRISDbits.TRISD0 = 0;
// Apagar analógico
ANSELD = 0;
}
155
Machine Translated by Google
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
servoRotar0();
servoRotar90();
servoRotar180();
regreso; } }
Una vez que todo va bien, el eje del servo se moverá a lo largo de un arco de 180
grados en incrementos de 90 grados, comenzando en 0 grados.
Una advertencia que debo mencionar sobre el uso real de servomotores es que
si compra la variedad barata, o incluso las de plástico de alta calidad, es
posible que en realidad no comiencen en 0 grados y terminen en 180 grados.
156
Machine Translated by Google
El motor paso a paso tiene múltiples bobinas que, cuando se organizan juntas,
se llaman fases. Cada fase se enciende secuencialmente, lo que hace que el
motor paso a paso gire. La distancia entre cada paso se conoce como el ángulo de
paso. Existen dos devanados principales para las bobinas del motor paso a paso. Uno se
conoce como unipolar y el otro como bipolar. La diferencia básica entre los dos es que,
en el motor paso a paso unipolar, hay una bobina con una derivación central por fase,
mientras que un motor bipolar tiene un devanado por fase.
En este libro, nos enfocamos en la variedad unipolar.
Hay tres formas principales de conducir un motor paso a paso. ahí está la ola
impulso, impulso de paso completo y impulso de medio paso.
En el modo de accionamiento por ondas, solo se activa una sola fase a la vez. Esto
da como resultado que el modo de conducción por ondas use menos energía que otros
modos, pero a costa de la pérdida de par. El modo de conducción de medio paso alterna
entre dos fases encendidas y una sola fase encendida. Esto aumenta la resolución
angular pero reduce el par. El modo de conducción de paso completo permite que dos
fases estén encendidas a la vez, lo que permite un par máximo.
Por lo tanto, cuando se diseña con motores paso a paso, existe un equilibrio entre el
par, el consumo de energía y la resolución angular.
Sin embargo, el método popular para accionar motores paso a paso es utilizar el
modo de accionamiento de paso completo; por lo tanto, este es el método que usaremos en este libro.
Para impulsar el motor paso a paso, debemos usar un controlador para darle al motor
paso a paso la potencia que necesita. Para motores paso a paso pequeños, puede usar
puentes H o matrices de transistores Darlington. Usaremos la matriz de Darlington ya que,
según mi experiencia, son más simples de usar.
157
Machine Translated by Google
158
Machine Translated by Google
159
Machine Translated by Google
/*
* Archivo: Principal.c
160
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
161
Machine Translated by Google
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/////////////////////
// Configurar
puertos ///////////////////
// Hacer salida D0
TRISD = 0;
// Apagar analógico
ANSELD = 0;
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
mientras(1){
LATD = 0b00001001; // Paso 1
__delay_ms(500);
LATD = 0b00000101; // Paso 2
__delay_ms(500);
162
Machine Translated by Google
Si observa el ciclo while principal en el Listado 7-5, notará que los cuatro
bits inferiores de PORTD siguen cambiando. En caso de que se esté preguntando
qué está pasando aquí, así es como funciona. ¿Recuerda que dijimos que en el
modo de paso de conducción completa, el motor enciende dos fases a la vez?
Bueno, si revisa el esquema de la Figura 7-3, notará que cada fase del motor
paso a paso está conectada a un pin del microcontrolador. Ahora mencioné que
las bobinas están conectadas al centro en el motor paso a paso de variedad
unipolar y, si observa su esquema, esto se muestra en la Figura 7-3. Por lo tanto,
se deben encender dos fases a la vez, ya que en realidad la corriente solo fluye
en la mitad del devanado del motor paso a paso a la vez. Por lo tanto, si observa
el código, notará que dos pines de salida están altos a la vez. Esto asegura que
al energizar alternativamente los medio devanados de las dos bobinas dentro del
motor paso a paso, obtenga la rotación completa de 360 grados del motor.
Si todavía tiene problemas para entender cómo funciona esto, hay excelentes
recursos en la web que profundizan en las complejidades del funcionamiento
de los motores paso a paso. En este libro, sin embargo, puede ignorar con
seguridad todos los detalles de la operación. Siempre que verifique con su hoja
de datos que ha conectado el motor paso a paso como se muestra en la Figura
7-3, este código funcionará con motores paso a paso unipolares de cualquier tamaño.
No se preocupe si su motor paso a paso se calienta. Los motores paso
a paso usan mucha corriente, y usan la misma cantidad de corriente cuando
están parados y en funcionamiento, así que no se asuste; esto es completamente normal.
163
Machine Translated by Google
Conclusión
Este capítulo analizó la interfaz de los actuadores con el microcontrolador
PIC®, incluidos los motores de CC, los motores paso a paso y los servomotores.
Estos forman la base para las aplicaciones de los microcontroladores PIC® en
áreas como la robótica y el control de motores.
164
Machine Translated by Google
CAPÍTULO 8
Interrupciones, Temporizadores,
Contadores y PWM
Introducción a las interrupciones
Las interrupciones son uno de los conceptos más simples relacionados con los microcontroladores.
Permítanme explicar las interrupciones de la manera más simple posible haciendo referencia a la vida cotidiana.
Imagina que necesitas despertarte a las 6:00 am. Hay dos formas de saber
cuándo tienes que despertarte. Una forma es seguir mirando el reloj hasta
que sean las 6:00. Sin embargo, si hace eso, entonces no podrá
concentrarse en la tarea en cuestión, que es dormir. La otra cosa que
puedes hacer es poner tu reloj en alarma para avisarte que son las 6:00. En
ambos escenarios, sabrá cuándo son las 6:00; sin embargo, el segundo
método es más efectivo ya que puede concentrarse en la tarea en cuestión, que es dormir.
Volviendo esta analogía a los microcontroladores, el primer
método para verificar continuamente su reloj es el método de sondeo. El
segundo método del reloj que le avisa cuando ha llegado el momento es
el método de interrupción. A las interrupciones se les asignan prioridades.
Por ejemplo, si está hablando por teléfono con alguien y su pareja lo llama,
puede interrumpir su llamada, hablar con su pareja y luego reanudar su
llamada. Esto se debe a que su pareja es más importante para usted y, por
lo tanto, se le asigna una mayor prioridad.
165
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_8
Machine Translated by Google
Una interrupción con mayor prioridad tiene prioridad sobre una de menor prioridad. El
La interrupción podría tener muchas fuentes, es decir, cosas que pueden provocar que
Ahora podría entrar en muchos detalles sobre cómo funcionan las interrupciones. Hay
Veamos la interrupción en acción, que se muestra en el Listado 8-1. En este caso, consideramos
que la interrupción externa usa un botón pulsador para activar una interrupción (vea la Figura
8-1).
166
Machine Translated by Google
/*
* Archivo: Main.c
* Autor: Armstrong Subero
* FOTO: 16F1717 con OSC interno @ 16MHz, 5v
*
Programa: 07_Interrupt_External *
Compilador: XC8 (v1.38, MPLAX X v3.40)
167
Machine Translated by Google
*
Versión del programa: 1.0
*
*
*
Descripción del programa: este programa permite que PIC16F1717 cambie
un LED basado
*
sobre el estado de una interrupción de botón pulsador
*
* Descripción del hardware: un LED está conectado a través de una
resistencia de 10k al PIN D1 y
* otro LED está conectado al PIN D2 y un interruptor está
* conectado al PIN B0
*
* Creado el 4 de noviembre de 2016 a las
20:10 */
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
168
Machine Translated by Google
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/////////////////////////
/// Configurar
puertos ////////////////////////
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar ANSELB0
ANSELBbits.ANSB0 = 0;
// desbloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS = 0xAA;
PPSLOCK = 0x00;
//////////////////////////
169
Machine Translated by Google
/// Configurar
interrupciones /////////////////////////
// bloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS = 0xAA;
PPSLOCK = 0x01;
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
170
Machine Translated by Google
while(1)
{ LATDbits.LATD1 = ~LATDbits.LATD1;
__retraso_ms(500); }
regreso;
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Interrupción disparada al presionar un botón
**************************************************** ************
***************/
// Toggle led
LATDbits.LATD2 = ~LATDbits.LATD2; }
171
Machine Translated by Google
Uso de temporizadores
convierte en un temporizador) o puede contar pulsos de reloj irregulares (en este modo es un
El temporizador necesita un pulso de reloj para funcionar. Esta fuente de reloj puede
ser interna o externa al microcontrolador. Cuando nos alimentamos internamente
borrar el bit TMR0CS del registro de opciones. Luego asignaremos un prescaler y para hacerlo,
necesitamos borrar el bit PSA del registro de opciones. En este ejemplo, usamos el
temporizador 0 para hacer parpadear un LED con precisión de 1 Hz. Para hacerlo, conecte el
LED al microcontrolador, como se muestra en la Figura 8-2 con RD1 conectado al LED a través
de una resistencia de 1k.
172
Machine Translated by Google
/*
* Archivo: Principal.c
173
Machine Translated by Google
*
Descripción del programa: este programa permite que PIC16F1717 parpadee
un LED a 1 Hz
* en Timer0 desbordamiento
*
* Descripción del hardware: un LED está conectado a través de una resistencia
de 10k al PIN D1
*
* Creado el 4 de noviembre de 2016 a las
16:14 */
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
// Variable de contador
conteo int = 0;
/**************************************************** ************
****************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
174
Machine Translated by Google
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
////////////////////////
/// Configurar
puertos //////////////////////
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar ANSELB0
ANSELBbits.ANSB0 = 0;
////////////////////////
/// Configurar
Timer0 ///////////////////////
OPTION_REGbits.TMR0CS = 0;
// Establecer Prescaler a
256 OPTION_REGbits.PS0
= 1; OPCIÓN_REGbits.PS1
= 1; OPCIÓN_REGbits.PS2 = 1;
175
Machine Translated by Google
// Temporizador cero
RTM0 = 0; }
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
mientras(1){
desbordamiento INTCONbits.TMR0IF = 0;
// Cuenta de incrementos
contar++;
176
Machine Translated by Google
si (cuenta == 61){
LATDbits.LATD1 = 1;
cuenta = 0;
}
regreso;
}
designe qué pin se usará para contar los pulsos del reloj externo. La secuencia
para asignar un periférico a un pin usando PPS es la siguiente:
1. Desbloquee el PPS.
3. Bloquee el PPS.
177
Machine Translated by Google
/*
* Archivo: Principal.c
178
Machine Translated by Google
*
Programa: 05_Contador
* Compilador: XC8 (v1.38, MPLAX X v3.40)
*
Versión del programa: 1.0
*
*
*
Descripción del programa: este programa permite que PIC16F1717 gire
en un LED
después de que * el módulo de contador en Time0 alcanza un valor
especificado * que se detecta a partir de pulsos externos dados a
través de un * interruptor en RB0.
*
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
179
Machine Translated by Google
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
////////////////////////
/// Configurar
puertos //////////////////////
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar ANSELB0
ANSELBbits.ANSB0 = 0;
////////////////////////
/// Configurar
Timer0 ///////////////////////
180
Machine Translated by Google
estado booleano =
GIE; GIE = 0;
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear PPS
GIE = estado;
// Temporizador cero
RTM0 = 0;
/**************************************************** ************
****************
***************/
181
Machine Translated by Google
(vacío) {
// Leer variable de valor
uint8_t readVal;
// valor devuelto
volver readVal; }
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
// variable de conteo
uint8_t cuenta;
mientras(1){
182
Machine Translated by Google
// enciende el LED
LATDbits.LATD1 = 1;
// temporizador cero
RTM0 = 0; }
demás
{ // mantener el LED
apagado LATDbits.LATD1
= 0; } }
regreso; }
Ahora miramos el uso del temporizador 0 con interrupciones. El código es muy simple
si ha estado siguiendo hasta este punto, y lo usaremos para hacer parpadear un LED
cada segundo mientras responde simultáneamente al botón para cambiar otro LED. Este
ejemplo demuestra una aplicación importante de las interrupciones, realizando una
acción de forma muy determinista.
conducta.
183
Machine Translated by Google
/*
* Archivo: Principal.c
184
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
185
Machine Translated by Google
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initMain()
{ //////////////////////
// Configurar
puertos /////////////////////
// Ejecutar a 16 MHz
interno_16();
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar ANSELB0
ANSELBbits.ANSB0 = 0;
////////////////////////
// Configurar
Timer0 //////////////////////
OPTION_REGbits.TMR0CS = 0;
186
Machine Translated by Google
// Establecer Prescaler a
256 OPTION_REGbits.PS = 0b111;
// habilitar interrupciones
globales ei(); }
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
while(1){ //
Alternar LED en el botón PUSH
LATDbits.LATD1 = ~PORTBbits.RB0; }
regreso; }
/**************************************************** ************
****************
187
Machine Translated by Google
* Devoluciones: Nada
*
*
Descripción: Timer0 interrumpe a una velocidad de 1 segundo
**************************************************** ************
***************/
desbordamiento INTCONbits.TMR0IF = 0;
RTM0 = 0;
// Cuenta de incrementos
contar++;
// De lo contrario, mantenga el
{ LATDbits.LATD2 = 0; }
188
Machine Translated by Google
Entendiendo PWM
La modulación de ancho de pulso (PWM) describe un tipo de señal que puede ser
producida por un microcontrolador. Sin embargo, para entender PWM, primero
debemos entender el concepto del ciclo de trabajo. Una señal digital puede ser 5v (alta) o
0v (baja). La cantidad de tiempo que una señal está alta se describe como el ciclo de
trabajo de esa señal. Esto se expresa como un porcentaje. Por ejemplo, si durante un
período de tiempo determinado, una señal está alta la mitad del tiempo y baja la otra
mitad, tendrá un ciclo de trabajo del 50 %.
Usando PWM
Veamos cómo usar el PWM en el módulo CCP. Se requiere un temporizador para usar el
módulo PWM. Dado que el temporizador 0 se usa para muchas cosas, usaremos el
temporizador 6 para que sea el temporizador PWM.
El módulo PWM es muy importante y tiene muchos usos. Los usos más populares
son la atenuación de la luz, el control de la velocidad del motor y la generación de una
señal modulada. En este ejemplo, usaremos el módulo PWM para atenuar un LED.
Haremos funcionar el LED al 50% del ciclo de trabajo.
La figura 8-5 muestra el circuito.
189
Machine Translated by Google
190
Machine Translated by Google
*
Descripción del Programa: Este Programa utiliza el módulo PWM del
FOTO16F1717
*
*
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
191
Machine Translated by Google
//////////////////////
// Configurar
Timer6 /////////////////////
CCPTMRSbits.C1TSEL = 0b10;
// Frecuencia de cristal
// Frecuencia PWM = -----------------------------------------
// (PRX + 1) * (Prescaler TimerX) * 4
// Preescala = 1
T6CONbits.T6CKPS = 0b00;
// Habilitar Temporizador6
T6CONbits.TMR6ON = 1;
PR6 = 255;
// Configurar CCP1
CCP1CONbits.CCP1M = 0b1100;
192
Machine Translated by Google
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
RB0PPSbits.RB0PPS = 0b01100;
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear
PPS }
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
while(1){ //
Ejecutar al 50 % del ciclo de trabajo a 15,625
kHz CCPR1L = 127; }
regreso; }
193
Machine Translated by Google
dos puentes H a bordo y puede manejar hasta 36 voltios a 1A por conductor. Además
de tener especificaciones decentes, este controlador también es de bajo costo, lo que
lo convierte en una opción atractiva para aplicaciones de propósito general.
Cuando se enseña a alguien a hacer algo por primera vez, es agradable
proporcione mucho agarre de la mano. Después de todo, los principiantes
necesitan mucha mano para comprender correctamente los conceptos. En este
proyecto, sin embargo, te dejo hacer un poco de investigación para completarlo.
Proporciono las conexiones en el texto y en el código, y su trabajo es conectar el
controlador del motor y el motor al microcontrolador sin un esquema. Crees que puedes manejar eso?
¡Vamos a empezar!
• 3: Motor 1 +
• 4-5: TIERRA
• 6: Motor 1
• 12-13: TIERRA
• 16: 5v
194
Machine Translated by Google
durante dos segundos y luego lo haremos funcionar de nuevo durante cinco segundos en la otra dirección.
195
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/**************************************************** ************
****************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/////////////////////
// Configurar
puertos ///////////////////
196
Machine Translated by Google
// Apagar analógico
ANSELB = 0;
//////////////////////
// Configurar
Timer6 /////////////////////
// Frecuencia de cristal
//Frecuencia PWM = -----------------------------------------
//(PRX + 1) * (Prescaler TimerX) * 4
// Preescala = 1
T6CONbits.T6CKPS = 0b00;
// Habilitar Temporizador6
T6CONbits.TMR6ON = 1;
PR6 = 255;
///////////////////////////
// Configurar
PWM /////////////////////////
// Configurar CCP1
197
Machine Translated by Google
CCP1CONbits.CCP1M = 0b1100;
// Configurar CCP2
CCP2CONbits.CCP2M = 0b1100;
///////////////////////////////
// Configurar
PPS /////////////////////////////
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
RB0PPSbits.RB0PPS = 0b01100;
RB1PPSbits.RB1PPS = 0b01101;
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear PPS
198
Machine Translated by Google
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
while(1){ //
Ejecutar a aprox. Ciclo de trabajo del 20 % a 15,625 kHz durante 5 segundos
// Hacia adelante
CCPR1L = 192;
CCPR2L = 0;
__retraso_ms(5000);
CCPR1L = 0;
CCPR2L = 0;
__retraso_ms(2000);
// Contrarrestar
CCPR1L = 0;
CCPR2L = 192;
__retraso_ms(5000);
}
regreso; }
199
Machine Translated by Google
200
Machine Translated by Google
/**************************************************** ************
****************
*Incluye y define
**************************************************** ************
***************/
#incluye "16F1717_Internal.h"
/*
Valor para PWM1
*/
void PWM1_LoadDutyValue(uint16_t dutyValue) {
// Escribir en 8 MSB del ciclo de trabajo pwm en el registro CCPRL
CCPR1L = ((valor de servicio y 0x03FC) >> 2);
/*
Valor para PWM2
*/
void PWM2_LoadDutyValue(uint16_t dutyValue) {
// Escribir en 8 MSB del ciclo de trabajo pwm en el registro CCPRL
CCPR2L = ((valor de servicio y 0x03FC) >> 2);
201
Machine Translated by Google
/*
Valor para PWM3
*/
void PWM3_LoadDutyValue(uint16_t dutyValue) {
// Escribir en 8 MSB del ciclo de trabajo PWM en el registro PWMDCH
PWM3DCH = (valor de servicio y 0x03FC) >> 2;
/*
Valor para LED
RGB */
void RGB_LoadValue(uint16_t rojo, uint16_t verde, uint16_t azul) {
PWM1_LoadDutyValue(rojo);
PWM2_LoadDutyValue(verde);
PWM3_LoadDutyValue(azul); }
/**************************************************** ************
****************
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************
***************/
202
Machine Translated by Google
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
/////////////////////
// Configurar
puertos ///////////////////
// Apagar analógico
ANSELB = 0;
//////////////////////
// Configurar
Timer6 /////////////////////
// Frecuencia de cristal
//Frecuencia PWM = -----------------------------------------
//(PRX + 1) * (Prescaler TimerX) * 4
203
Machine Translated by Google
// Preescala = 1
T6CONbits.T6CKPS = 0b00;
// Habilitar Temporizador6
T6CONbits.TMR6ON = 1;
PR6 = 255;
///////////////////////////
// Configurar
PWM /////////////////////////
// Configurar CCP1
CCP1CONbits.CCP1M = 0b1100;
// Configurar CCP2
CCP2CONbits.CCP2M = 0b1100;
// Configurar PWM 3
204
Machine Translated by Google
// PWM3DCH127
PWM3DCH = 0x7F;
// PWM3DCL 192
PWM3DCL = 0xC0;
// Seleccionar temporizador6
CCPTMRSbits.P3TSEL = 0b10;
///////////////////////////////
// Configurar
PPS /////////////////////////////
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
RB0PPSbits.RB0PPS = 0b01100;
RB1PPSbits.RB1PPS = 0b01101;
// Establecer RB2 en
PWM3 RB2PPSbits.RB2PPS = 0x0E;
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear PPS
205
Machine Translated by Google
/**************************************************** ************
****************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************
***************/
vacío principal
(vacío) { initMain ();
mientras(1){
// Rojo
RGB_LoadValue(512, 0, 0);
__retraso_ms(1000);
// Verde
RGB_LoadValue(0, 512, 0);
__retraso_ms(1000);
// Azul
RGB_LoadValue(0, 0, 512);
__retraso_ms(1000);
// Amarillo
RGB_LoadValue(192, 192, 0);
__retraso_ms(1000);
206
Machine Translated by Google
// Púrpura
RGB_LoadValue(192, 0, 192);
__retraso_ms(1000);
// aguamarina
RGB_LoadValue(0, 512, 512);
__retraso_ms(1000);
}
regreso;
}
Conclusión
Esto concluye este capítulo, donde vimos algunos módulos integrados
más del microcontrolador PIC®. Examinamos las interrupciones, que permiten
que el microcontrolador salte instantáneamente a una tarea específica, los
temporizadores, que cuentan pulsos de reloj regulares, los contadores, que
cuentan pulsos irregulares y PWM, que es muy útil en aplicaciones de
microcontroladores como iluminación y control de motores.
207
Machine Translated by Google
CAPÍTULO 9
209
© Armstrong Subero
2018 A. Subero, Programación de microcontroladores
PIC con XC8, https://doi.org/10.1007/978-1-4842-3273-6_9
Machine Translated by Google
tiene lugar sin reloj. Esto ahorra un pin de E/S ya que en este modo solo se requieren las líneas
modo síncrono permite que el módulo se use con un reloj y no se usa tanto, por lo que no lo
del USART especifica la velocidad a la que el USART transfiere datos. Una tasa de baudios de
2400 significa que el USART puede transferir un máximo de 2400 bits
por segundo.
El módulo LCD serial que usaremos es el LCD Parallax 2x16 (vea la Figura 9-1 ). En este ejemplo,
Esta pantalla LCD también incluye un altavoz piezoeléctrico incorporado y tiene luz de fondo.
210
Machine Translated by Google
/*
* Archivo: EUSART.h
/**************************************************** ************
* Función Prototipo
**************************************************** ************/
211
Machine Translated by Google
/**************************************************** ************
*Incluye y define
**************************************************** ***********/
#incluye "16F1717_Internal.h"
#include "EUSART.h"
212
Machine Translated by Google
/**************************************************** ************
* Devoluciones: Nada
*
*
Descripción: Inicializa el módulo EUSART
*
*
Uso: EUSART_Initialize()
************************************************ ****************/
BRGH = 1;
} si(x<256)
{
SPBRG = x;
SINCRONIZACIÓN = 0;
GASTO = 1;
TRISC7 = 1;
TRISC6 = 1;
CREN = 1;
TXEN = 1;
devolver 1;
} devuelve 0;
213
Machine Translated by Google
/**************************************************** ************
* Función: char EUSART_Read (void)
*
* Devoluciones: Nada
*
*
Descripción: Lee el módulo EUSART
*
*
Uso: EUSART_Read()
******************************************** ****************/
uint8_t EUSART_Read(vacío)
{
RC1STAbits.SREN =
1; while(!PIR1bits.RCIF)
{}
si (1 == RC1STAbits.OERR)
{
//Error EUSART – reiniciar
RC1STAbits.SPEN =
0; RC1STAbits.SPEN = 1;
}
devolver RC1REG;
}
// Leer caracteres
214
Machine Translated by Google
/**************************************************** ************
* Función: char EUSART_Write (uint8_t txData)
*
* Devoluciones: Nada
*
*
Descripción: Escribe en el módulo EUSART
*
*
Uso: EUSART_Write(x)
**************************************************** ***********/
ent
yo; for(int i=0;i<longitud;i++)
Salida[i] = EUSART_Read();
}
/**************************************************** ************
* Función: char EUSART_Write_Text (char *texto)
*
* Devoluciones: Nada
*
215
Machine Translated by Google
*
Descripción: Escribe texto el módulo EUSART
*
*
Uso: EUSART_Write_Text("Alguna cadena")
**************************************************** ***********/
/*
* Archivo: Principal.c
* Descripción del hardware: una pantalla LCD Parallax 16x2 está conectada al PIN
RB2 de la
216
Machine Translated by Google
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
#incluye "EUSART.h"
/**************************************************** ************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
//////////////////////
// Configurar
PIN ////////////////////
TRISBbits.TRISB2 = 0;
ANSELBbits.ANSB2 = 0;
217
Machine Translated by Google
//////////////////////
// Configurar
EUSART /////////////////////
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear
PPS }
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************/
mientras(1){
// Enviar comando
218
Machine Translated by Google
// Enviar texto
EUSART_Write_Text("Hola");
regreso;
Comunicación USART a PC
Cuando necesite comunicarse con una PC, puede usar un convertidor
de serie a USB. Hay algunos microcontroladores que tienen USB
integrado; sin embargo, la comunicación USB es muy compleja y
requiere que los usuarios escriban su propia pila o usen pilas (a veces
poco confiables) provistas por el fabricante. Al usar un puente UART a
USB, puede evitar muchos dolores de cabeza. El CP2104 es excelente y lo recomiendo much
Texto a voz
Ahora veremos la síntesis de voz utilizando un módulo de texto a voz (TTS).
Los módulos TTS convierten el texto en voz hablada. El módulo TTS que
usaremos es el módulo EMIC 2 TTS (consulte la Figura 9-2). Este módulo
es muy fácil de usar. Permite al usuario seleccionar muchas voces y
produce una voz que es muy fácil de entender. Una vez que haya terminado
de ejecutar el código de esta sección, descargue la hoja de datos del
módulo. Experimente con diferentes voces y juegue un poco con el módulo.
Esta es una buena práctica. Una vez que haya leído las hojas de datos de
los módulos y sensores, puede crear sus propias bibliotecas y código, sin
quedarse atascado si no puede encontrar una biblioteca en línea.
219
Machine Translated by Google
El archivo de encabezado sigue siendo el mismo. El código principal se muestra en el Listado 9-4.
/*
* Archivo: Principal.c
220
Machine Translated by Google
* RB2-> SIN;
* RB3-> SUR;
*
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
#incluye "EUSART.h"
/**************************************************** ************
* Función: void initMain()
*
Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ***********/
221
Machine Translated by Google
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
// Configurar
PINS TRISBbits.TRISB3
= 1; ANSELBbits.ANSB3 = 0;
TRISBbits.TRISB2 = 0;
ANSELBbits.ANSB2 = 0;
//////////////////////
// Configurar
EUSART /////////////////////
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear
PPS }
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ***********/
222
Machine Translated by Google
lectura de charEmic;
EUSART_Escribir(13);
mientras(1){
// Di algo
EUSART_Escribir(83);
EUSART_Write_Text("Hola");
EUSART_Escribir(13);
__retraso_ms(500); }
regreso;
223
Machine Translated by Google
En este ejemplo, analizamos el uso del módulo GPS U-BLOX NEO-6M, ya que es muy
fácil de usar y, en el momento de escribir este artículo, está ampliamente disponible y tiene
un costo extremadamente bajo (consulte la Figura 9-3).
Comandos NMEA
Para usar el módulo GPS de manera efectiva, debemos comprender cómo funcionan
los datos NMEA transmitidos por el módulo GPS. NMEA significa Asociación
Nacional de Electrónica Marina y ese organismo produjo una especificación que
permite que el receptor GPS brinde los datos de tiempo, posición y velocidad que
el usuario puede analizar. El receptor GPS envía estos comandos como oraciones en
un formato particular.
224
Machine Translated by Google
$GPGLL,1123.01,N,1234.00,W,000111,A,*65
/*
* Archivo: Principal.c
225
Machine Translated by Google
*
Descripción del programa: este programa permite que PIC16F1717
comunicarse a través de la
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
#incluye "EUSART.h"
#incluir "LCD.h"
#incluir <cadena.h>
// Variables
carbón volátil c;
carbón volátil d;
char* datos;
226
Machine Translated by Google
terminador char*;
char conversionString[8];
// Prototipo de función
void read_gps();
/**************************************************** ************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
// Configurar
PINS TRISBbits.TRISB3
= 1; ANSELBbits.ANSB3 = 0;
TRISBbits.TRISB2 = 0;
ANSELBbits.ANSB2 = 0;
227
Machine Translated by Google
TRISD = 0;
ANSELD = 0;
PUERTO = 0;
//////////////////////
// Configurar
EUSART /////////////////////
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear PPS
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************/
228
Machine Translated by Google
mientras(1){
Lcd_Set_Cursor(1,1);
leer_gps();
// Escribir Latitud
Lcd_Write_Float(*latitud);
Lcd_Set_Cursor(2,1);
// Escribir Longitud
Lcd_Write_Float(*longitud);
__retraso_ms(2000);
Lcd_Borrar(); }
regreso;
229
Machine Translated by Google
/**************************************************** ************
* Función: anular read_gps()
*
* Devoluciones: punteros a lat y lon
*
*
Descripción: Función para leer el módulo GPS
*
*
Uso: read_gps()
**************************************************** ***********/
anular leer_gps(){
// si es nulo exit
if(data == NULL)
{ return; }
// Encuentra el
terminador terminador = strstr(datos,",");
230
Machine Translated by Google
// si es nulo exit
if(terminator == NULL)
{ return; }
if(datos[7] == ',')
{ retorno; }
////////////////////////////////////
// Buscar datos de búfer para valores
de latitud // y longitud ///////////////////////////////////
datos = terminador+1;
terminador = strstr(datos,",");
si (terminador == NULL)
{ retorno; }
memset(conversionString,0,sizeof(conversionString));
memcpy(conversionString, datos, 2); *latitud =
atof(conversionString);
datos +=
2; *terminador = '\0';
*latitud += (atof(datos)/60);
231
Machine Translated by Google
datos = terminador+1;
terminador = strstr(datos,","); si
(terminador == NULL) { retorno; }
if(*datos == 'S') { *latitud *= -1; }
datos = terminador+1;
terminador = strstr(datos,","); si
(terminador == NULL) { retorno; }
memset(conversionString,0,sizeof(conversionString));
memcpy(conversionString, datos, 3); *longitud =
atof(conversionString);
datos += 3;
*terminador = '\0';
*longitud += (atof(datos)/60);
datos = terminador+1;
terminador = strstr(datos,","); si
(terminador == NULL) { retorno; }
232
Machine Translated by Google
si(*datos == 'W')
{
*longitud *= -1;
}
Software USART
El PIC16F1717 solo tiene un módulo USART que los usuarios pueden
usar en sus aplicaciones. Ahora puede haber situaciones en las que
necesite usar más de un módulo USART para su aplicación. En este
caso, es posible que deba rediseñar su circuito y utilizar un chip que tenga más funciones.
Sin embargo, esto no es tan simple como parece, porque es posible que ya
esté familiarizado con el chip que está utilizando para su aplicación y es
posible que no desee asumir los costos asociados con el uso de un chip
más grande. En tales casos, puede ser útil usar una implementación de
software USART (también llamada "bit-banged").
El USART bit-banged que usamos en este ejemplo funciona de manera confiable hasta
aproximadamente 2400 baudios. Las velocidades de bits más altas pueden ser inestables y es
posible que no funcionen según lo previsto.
Módulo GSM
Veremos una aplicación del USART bit-banged utilizando un módulo
GSM. Usaremos el módulo SIM 800L (vea la Figura 9-4), que es muy
popular en el momento de escribir este artículo y es muy fácil de usar.
233
Machine Translated by Google
Comandos AT
En una sección anterior, analizamos los datos estructurados en formato NMEA, que es
el tipo de datos que suelen proporcionar los módulos GSM. En esta sección, analizamos
el uso de comandos AT (atención). Los comandos AT se usan comúnmente para
controlar módems. Estos son algunos comandos AT de uso común para controlar el
módulo SIM 800L:
234
Machine Translated by Google
en lugar del carácter LCD. Si no desea hacer esto, puede pasar a la sección
más adelante en este capítulo titulada “Carácter: la pantalla LCD Hitachi
HD44780” y usar el código de la pantalla LCD de caracteres en esa sección.
/*
* Archivo: Principal.c
*5v -> 5V
*TIERRA -> TIERRA
*VDD -> NC
*SIM_TXD -> RX
* SIM_RXD -> TX
*TIERRA -> TIERRA
*
*
235
Machine Translated by Google
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
#incluye "LCD.h"
// Configuración para
UART suave #define Baudrate
2400 //bps #define OneBitDelay (1000000/
Baudrate) #define DataBitCount 8 // sin paridad, sin
control de flujo #define UART_RX LATEbits.LATE0// UART
RX pin #define UART_TX LATEbits.LATE1 // Pin UART TX
#define UART_RX_DIR TRISE0// Registro de dirección de pin
UART RX #define UART_TX_DIR TRISE1 // Registro de dirección de pin UART TX
//Declaraciones de
funciones void
InitSoftUART(void); carácter sin
firmar UART_Receive(void); void
UART_Transmit(const char); void
SUART_Write_Text(char *texto);
vacío SUART_Write_Char(char a); void SUART_Read_Text(char *Salida, longitud int sin
/**************************************************** ************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************/
236
Machine Translated by Google
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
// Configurar pines
TRISD = 0x00;
ANSELD = 0x00;
PUERTO = 0x00;
TRISE = 0;
ANSELE = 0; }
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************/
Lcd_Inicio();
__retraso_ms(1000);
mientras(1){
SUART_Write_Text("AT+CMGF=1\r\n");
__retraso_ms(1000);
237
Machine Translated by Google
// Mensaje a enviar
SUART_Write_Text("Prueba");
__retraso_ms(1000);
UART_Transmit((char)26);
__retraso_ms(1000);
// Escribir cadena
Lcd_Write_String("Enviado");
__retraso_ms(2000);
regreso; }
/*
* Iniciar SW UART
*/
void InitSoftUART(void) // Inicializa los pines UART a los valores
adecuados
{
UART_TX = 1; // El pin TX está alto en estado inactivo
UART_RX_DIR = 1; // Aporte
UART_TX_DIR = 0; // Producción }
238
Machine Translated by Google
/*
* Recibir a través de SW UART
*/
char sin firmar UART_Receive(void) {
// Configuraciones de pines
// GP1 es pin UART RX
__delay_us(OneBitDelay);
__delay_us(OneBitDelay/2); // Toma el valor de la muestra a la mitad de la
duración del bit
ValorDatos += (1<<i); }
__delay_us(OneBitDelay); }
// Comprobar el bit de
parada si ( UART_RX == 1 ) // El bit de parada debe ser
alto { __delay_us(OneBitDelay/2); devolver valor de
datos; } else //¡ocurrió algún error!
239
Machine Translated by Google
{ __delay_us(OneBitDelay/2);
devolver 0x000; } }
/*
* Transmitir a través de SW UART
*/
void UART_Transmit(const char DataValue) {
/* Lógica básica
El pin TX suele ser alto. Un bit alto a bajo es el bit inicial y un bit bajo a alto
es el bit final. Sin bit de paridad. Sin control de flujo.
BitCount es el número de bits a transmitir. Los datos se transmiten LSB
primero.
*/
240
Machine Translated by Google
UART_TX =
0; }
__delay_us(OneBitDelay); }
/*
* Escribir texto a través de SW UART
*/
void SUART_Write_Text(char *texto)
{ int i;
for(i=0;texto[i]!='\0';i++)
UART_Transmit(texto[i]); }
/*
* Leer texto a través de SW UART
*/
void SUART_Read_Text(char *Salida, longitud int sin signo) { int
i; for(int i=0;i<longitud;i++)
241
Machine Translated by Google
{
Salida[i] = UART_Receive();
}
}
/*
* Escribir Char a través de SW UART
*/
vacío SUART_Write_Char(char a)
{
UART_Transmitir (a - 0x128);
}
242
Machine Translated by Google
/*
* Archivo: SPI.h
* Autor: Armstrong Subero
* FOTO: 16F1717 con X OSC @ 16MHz, 5v
*
Programa: archivo de encabezado para el módulo SPI
* Compilador: XC8 (v1.38, MPLAX X v3.40)
*
*
Versión del programa 1.0
*
*
Descripción del programa: este encabezado de programa proporciona funciones
prototipos para
* Módulo SPI en PIC16F1717
*
*
*
243
Machine Translated by Google
/**************************************************** ************
*Incluye y define
**************************************************** ************/
#incluye "16F1717_Internal.h"
#define DUMMY_DATA 0x0
Potenciómetro digital
El potenciómetro digital que usamos para demostrar el uso del bus SPI es el
MCP4131. El MCP4131 es un dispositivo de 7 bits que proporciona un total de
129 valores diferentes y se controla a través de SPI. En este ejemplo, enviamos
comandos al dispositivo y el dispositivo ajusta su resistencia. El cambio en la
resistencia es visible al conectar el pin POW del dispositivo a un LED a través de una resistencia de 1
La resistencia entonces variará en brillo.
El listado 9-8 proporciona el código principal.
/*
* Archivo: Principal.c
244
Machine Translated by Google
*
Versión del programa: 1.0
*
*
*
Descripción del programa: este programa permite que PIC16F1717
comunicarse a través de la
* Interfaz SPI
*
*
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
#incluye "LCD.h"
#incluir "SPI.h"
245
Machine Translated by Google
/**************************************************** ************
* Función: void initMain()
*
* Devoluciones: Nada
*
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
// Apagar LED
LATDbits.LATD1 = 0;
// Configurar PUERTO
TRISD = 0;
ANSELD = 0;
// Inicializar LCD
Lcd_Inicio();
__retraso_ms(100);
Lcd_Borrar();
246
Machine Translated by Google
ANSELCbits.ANSC5 = 0;
TRISCbits.TRISC3 = 0;
TRISCbits.TRISC4 = 1;
TRISCbits.TRISC5 = 0;
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear PPS
// Inicializar SPI
SPI_Inicializar();
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************/
vacío principal
(vacío) { initMain ();
247
Machine Translated by Google
Lcd_Set_Cursor(1,1);
__retraso_ms(5);
Lcd_Write_String("Listo para
SPI"); __retraso_ms(1000);
Lcd_Borrar();
mientras(1){
regreso;
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Escribe un valor particular en un potenciómetro digital
MCP4131
*
*
Uso: digiPot_write(x);
**************************************************** ************/
248
Machine Translated by Google
LATDbits.LATD1 = 0;
SPI_Exchange8bit(0x00);
// Datos
SPI_Exchange8bit(yo);
// Establecer SS alto
LATDbits.LATD1 = 1;
}
Visualización de caracteres
Aunque el enfoque del siguiente capítulo son las pantallas, utilizamos una
pantalla LCD de caracteres simples para demostrar el protocolo I2C.
La pantalla LCD comúnmente tiene 14 pines; sin embargo, las pantallas LCD
que tienen luz de fondo tienen 16 pines. Recomiendo la versión con la luz de fondo.
Descargue la hoja de datos de su pantalla en particular para determinar qué versión
de pantalla tiene.
Veamos el código para usar la pantalla LCD HD44780.
Primero, el Listado 9-9 muestra el archivo de encabezado.
249
Machine Translated by Google
/*
* Archivo: LCD.h
* Autor: Armstrong Subero
* FOTO: 16F1717 con X OSC @ 16MHz, 5v
*
Programa: Archivo de
encabezado para * Compilador: XC8 (v1.38, MPLAX X v3.40)
*
Versión del programa
1.1 * *Comentarios adicionales agregados
*
*
Descripción del programa: este encabezado de programa proporciona rutinas
para controlar
*
un STD HITACHI HD44780 y LCD compatibles
*
250
Machine Translated by Google
/**************************************************** ************
*Incluye y define
********************************************** ****************/
// STD XC8 incluir
#incluir <xc.h>
// Operación de 4
bits #define D4 RD4 //Bit 4
#define D5 RD5 //Bit 5
#define D6 RD6 //Bit 6
#define D7 RD7 //Bit 7
// prototipos de funciones
void Lcd_Port(char a);
vacío Lcd_Cmd(char a);
vacío Lcd_Clear(); void
Lcd_Set_Cursor(char a, char b); vacío
Lcd_Init(); vacío Lcd_Write_Char(char a);
void Lcd_Write_String(const char *a);
vacío Lcd_Shift_Right(); vacío
Lcd_Shift_Left(); vacío Lcd_Write_Integer(int
v); vacío Lcd_Write_Float (flotante f);
251
Machine Translated by Google
/*
* Archivo: LCD.c
* Autor: Armstrong Subero
* FOTO: 16F1717 con OSC interno @ 16MHz, 5v
*
Programa: archivo de biblioteca para configurar PIC16F1717
* Compilador: XC8 (v1.38, MPLAX X v3.40)
*
Versión del programa:
1.1 * *Comentarios adicionales agregados
*
*
Descripción del programa: esta biblioteca le permite interactuar
HD44780 y *
LCD compatibles
*
#incluir "LCD.h"
#incluye "16F1717_Internal.h"
/**************************************************** ************
* Función: void Lcd_Port (char a)
*
* Devoluciones: Nada
*
*
Descripción: Rutinas de configuración de LCD
**************************************************** ************/
void Lcd_Port(char a)
{ if(a & 1)
D4 = 1;
252
Machine Translated by Google
demás
D4 = 0;
si (a & 2)
D5 = 1;
demás
D5 = 0;
si (a & 4)
D6 = 1;
demás
D6 = 0;
si (a y 8)
D7 = 1;
demás
D7 = 0; }
/**************************************************** ************
* Función: void Lcd_Cmd (char a)
*
* Devoluciones: Nada
*
*
Descripción: Establece el comando
LCD ******************************************** ****************/
vacío Lcd_Cmd(char a)
{
RS = 0; // => RS = 0
puerto_lcd(a);
ES = 1; // => E = 1
__delay_ms(1);
ES = 0; // => E = 0 }
253
Machine Translated by Google
/**************************************************** ************
* Función: anular Lcd_Clear()
*
* Devoluciones: Nada
*
*
Descripción: Borra la pantalla
LCD ******************************************** ****************/
vacío Lcd_Clear()
{
Lcd_Cmd(0);
Lcd_Cmd(1); }
/**************************************************** ************
* Función: void Lcd_Set_Cursor(char a, char b)
*
* Devoluciones: Nada
*
*
Descripción: Establece la posición del cursor LCD
**************************************************** ************/
Lcd_Cmd(z);
Lcd_Cmd(y); }
254
Machine Translated by Google
de lo contrario si (a
== 2) { temp = 0xC0
+ b - 1; z = temperatura>>4;
y = temperatura y 0x0F;
Lcd_Cmd(z);
Lcd_Cmd(y); } }
/**************************************************** ************
* Función: anular Lcd_Init()
*
* Devoluciones: Nada
*
*
Descripción: Inicializa la pantalla LCD
**************************************************** ************/
vacío Lcd_Init() {
Puerto_Lcd(0x00);
__retraso_ms(10);
Lcd_Cmd(0x03);
__retraso_ms(3);
Lcd_Cmd(0x03);
__retraso_ms(10);
255
Machine Translated by Google
Lcd_Cmd(0x0C);
Lcd_Cmd(0x00);
Lcd_Cmd(0x06); }
/**************************************************** ************
* Función: void Lcd_Write_Char (char a)
*
* Devoluciones: Nada
*
*
Descripción: Escribe un carácter en la pantalla LCD
**************************************************** ************/
void Lcd_Write_Char(char a)
{ char temp,y; temperatura =
a&0x0F; y = a&0xF0;
RS = 1; // => RS = 1
Puerto_Lcd(y>>4); //Transferencia de datos
ES = 1;
__delay_us(20);
ES = 0;
Lcd_Port(temp);
ES = 1;
__delay_us(20);
ES =
0; }
256
Machine Translated by Google
/**************************************************** ************
* Función: void Lcd_Write_String (const char *a)
*
* Devoluciones: Nada
*
*
Descripción: Escribe una cadena en la
pantalla LCD ****************************************** *******************/
void Lcd_Write_String(const char *a)
{ int i; para(i=0;a[i]!='\0';i++)
Lcd_Write_Char(a[i]); }
/**************************************************** ************
* Función: anular Lcd_Shift_Right()
*
* Devoluciones: Nada
*
*
Descripción: Cambia el texto en la pantalla LCD a la derecha
**************************************************** ************/
vacío Lcd_Shift_Right()
{
LCD_Cmd(0x01);
Lcd_Cmd(0x0C); }
257
Machine Translated by Google
/**************************************************** ************
* Función: anular Lcd_Shift_Left()
*
* Devoluciones: Nada
*
*
Descripción: desplaza el texto de la pantalla
LCD a la izquierda ****************************************** *******************/
vacío Lcd_Shift_Left() {
LCD_Cmd(0x01);
Lcd_Cmd(0x08); }
/**************************************************** ************
* Función: void Lcd_Write_Integer(int v)
*
* Devoluciones: Nada
*
*
Descripción: Convierte una cadena en un entero
**************************************************** ************/
void Lcd_Write_Integer(int v)
{ unsigned char buf[8];
Lcd_Write_String(itoa(buf, v, 10)); }
258
Machine Translated by Google
/**************************************************** ************
* Función: void Lcd_Write_Float(float f)
*
* Devoluciones: Nada
*
*
Descripción: Convierte una cadena en un flotante
**************************************************** ************/
void Lcd_Write_Float(float f)
{ char* buf11; estado int;
Lcd_Write_String(buf11); }
Finalmente, el Listado 9-11 proporciona el código fuente que pasa por todos los
rutinas
/*
* Archivo: Principal.c
259
Machine Translated by Google
*
Descripción del programa: este programa permite que
PIC16F1717 interactúe con
* HD44780 y LCD compatibles
*
*
* RS ---> RD2
* L/E ---> TIERRA
* ES ---> RD3
* D4---> RD4
* D5---> RD5
* D6---> RD6
* D7---> RD7
*
*
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
#incluye "LCD.h"
/**************************************************** ************
* Función: void initMain()
*
* Devoluciones: Nada
*
260
Machine Translated by Google
*
Descripción: Contiene inicializaciones para main
*
*
Uso: initMain()
**************************************************** ************/
void initPrincipal(){
// Ejecutar a 16 MHz
interno_16();
TRISD = 0x00;
ANSELD = 0x00;
PUERTO = 0x00; }
/**************************************************** ************
* Función: Principal
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************/
vacío principal
(vacío) { initMain ();
Lcd_Inicio();
en un;
intc;
flotador b;
mientras(1){
Lcd_Borrar();
Lcd_Set_Cursor(1,1);
261
Machine Translated by Google
// Escribir cadena
Lcd_Write_String("PIC16F1717");
// Desplazarlo a la
izquierda
para(a=0;a<15;a++)
{ __delay_ms(500);
Lcd_Shift_Left(); }
// Desplazarlo a la
derecha para
(a=0;a<15;a++)
{ __delay_ms(500);
Lcd_Shift_Derecha(); }
Lcd_Borrar();
Lcd_Set_Cursor(1,1);
// Escribir entero
para (c = 0; c < 100; c++){
Lcd_Write_Integer(c);
__retraso_ms(300);
Lcd_Borrar();
__retraso_ms(15);
// Escribir Flotante
262
Machine Translated by Google
__retraso_ms(15);
}
__retraso_ms(1000);
}
regreso;
}
El Samsung KS0066U
El código del Listado 9-11 funciona en HD44780 y LCD compatibles,
incluido el Samsung KS0066U, y se probó en ambas pantallas.
I2C es único en comparación con los otros protocolos que hemos discutido
hasta ahora en que solo se usa una línea para el flujo de datos. En el bus I2C, el
dispositivo conocido como maestro se utiliza para comunicarse con otro dispositivo, conocido
como el esclavo. El maestro puede comunicarse con el esclavo porque cada
dispositivo en el esclavo tiene su propia dirección.
263
Machine Translated by Google
Las líneas utilizadas para la comunicación con el esclavo son las seriales.
línea de reloj (SCL) y línea de datos en serie (SDA). Para que I2C funcione,
las líneas deben estar conectadas a VCC mediante resistencias pull-up. Hay
cálculos que se pueden usar para determinar el valor de estas resistencias
calculando la capacidad de las líneas. En la práctica, sin embargo, he descubierto
que las resistencias de 4,7 K o 1 K hacen el trabajo de manera bastante adecuada.
Estas resistencias pull-up son necesarias porque los dispositivos I2C extraen la señal
línea baja pero no puede subirla, y las resistencias pull-up están ahí para
restaurar la línea de señal a alta.
La velocidad que utilizan la mayoría de los dispositivos I2C para comunicarse es de 100 kHz o
400 kHz. El protocolo transmite información a través de tramas. Hay dos tipos
de marcos disponibles: un marco de dirección, que informa al bus qué dispositivos
esclavos recibirán el mensaje, seguido de marcos de datos que contienen los datos
reales de 8 bits. Cada cuadro tiene un noveno bit, llamado bit de reconocimiento
(ACK) o no reconocimiento (NACK), que se utiliza para indicar si el dispositivo
esclavo lee la transmisión.
EEPROM
El ejemplo que usamos para I2C es conectar el microcontrolador con un dispositivo
EEPROM basado en I2C. El archivo de encabezado se muestra en el Listado 9-12.
/*
* Archivo: I2C.h
264
Machine Translated by Google
*
Programa: archivo de encabezado para configurar PIC16F1717 I2C
* Compilador: XC8 (v1.35, MPLAX X v3.10)
*
Versión del programa
1.2 * Archivo separado en encabezado y archivo
fuente C * Código no mcc usado
*
*
*
Descripción del programa: este encabezado de programa permitirá la
configuración de I2C
*
/**************************************************** ************
*Incluye y define
********************************************** ****************/
#incluye "16F1717_Internal.h"
265
Machine Translated by Google
/*
* Archivo: I2C.c
* Autor: Armstrong Subero
* FOTO: 16F1717 con OSC interno @ 16MHz, 5v
*
Programa: archivo de biblioteca para configurar el módulo PIC16F1717 I2C
* Compilador: XC8 (v1.38, MPLAX X v3.40)
*
Versión del programa:
1.1 * *Comentarios adicionales agregados
*
*
Descripción del programa: esta biblioteca le permite controlar
PIC16F1717 I2C
*
#incluir "I2C.h"
//**************************************************** ************\
********
266
Machine Translated by Google
// **************************************************** ***********\
************
//**************************************************** ************\
********
267
Machine Translated by Google
//**************************************************** ************\
********
//**************************************************** ************\
********
268
Machine Translated by Google
// Reúna el byte de control del código del dispositivo, bloquee los bits
de dirección y el bit R/W
// por lo que se ve así: CCCCBBBR
// donde 'CCCC' es el código de control del dispositivo
// 'BBB' es la dirección del bloque
// y 'R' es el bit de lectura/escritura
//**************************************************** ************\
********
269
Machine Translated by Google
void Send_I2C_StartBit(void)
{ PIR1bits.SSP1IF=0; // borra
el bit de interrupción del SSP
SSPCON2bits.SEN=1; // enviar bit de inicio while(!
PIR1bits.SSP1IF); // Espere a que el bit SSPIF vuelva a estar alto
antes de cargar el búfer de datos }
//**************************************************** ************\
********
//**************************************************** ************\
********
// Enviar bit ACK a VER //
******************************************** *******************\
********
270
Machine Translated by Google
//**************************************************** ************\
********
271
Machine Translated by Google
/*
* Archivo: Principal.c
/**************************************************** ************
*Incluye y define
**************************************************** ************/
#incluye "16F1717_Internal.h"
#incluye "LCD.h"
#incluir "I2C.h"
272
Machine Translated by Google
/**************************************************** ************
*Incluye y define
**************************************************** ************/
/**************************************************** ************
* Función: vacío initmain (vacío)
*
* Devoluciones: Nada
*
*
Descripción: Inicializaciones para main
**************************************************** ************/
vacío initmain(vacío)
{ internal_16();
TRISCbits.TRISC4 = 1;
TRISCbits.TRISC5 = 1;
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x00; // desbloquear PPS
273
Machine Translated by Google
PPSLOCK = 0x55;
BLOQUEO PPS =
0xAA; PPSLOCKbits.PPSLOCKED = 0x01; // bloquear
PPS // Configuración para LCD TRISD = 0; ANSELD =
0;
//Configurar LCD
Lcd_Inicio();
__retraso_ms(1000);
Lcd_Borrar();
/**************************************************** ************
* Función: vacío principal (vacío)
*
* Devoluciones: Nada
*
*
Descripción: Punto de entrada del programa
**************************************************** ************/
vacío principal
(vacío) { initmain
();
I2C_Init();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Listo para I2C");
__retraso_ms(1000);
Lcd_Borrar();
274
Machine Translated by Google
mientras
(1)
{ ////////////////////////////
// Escribir EEPROM
///////////////////////////
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Escribir");
__retraso_ms(1000);
Lcd_Borrar();
/////////////////////////////
// Leer EEPROM
////////////////////////////
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Leer");
__retraso_ms(1000);
Lcd_Borrar();
275
Machine Translated by Google
Lcd_Set_Cursor(1,1);
Lcd_Write_Integer(incoming_data);
__retraso_ms(1000);
Lcd_Borrar();
}
}
Conclusión
En este capítulo, analizamos USART, SPI e I2C, que son los
protocolos de comunicación fundamentales de los sistemas basados en microcontroladores.
También analizamos GPS, GSM, LCD y muchas otras cosas. Una vez que
comprenda estos protocolos de comunicación, puede conectar fácilmente
su microcontrolador a una gran cantidad de sensores. En este punto puedes
hacer mucho; sin embargo, siga leyendo porque los próximos capítulos
llevarán sus habilidades a otro nivel.
276