Documentos de Académico
Documentos de Profesional
Documentos de Cultura
MCU´s EN LENGUAJE C
(CCS C COMPILER)
Parte 3
Por: Miguel Angel Montilla G.
EEPROM DE DATOS:
ESCRITURA: LECTURA:
WRITE_EEPROM(DIRECCIÓN, VALOR) VALOR= READ_EEPROM(DIRECCIÓN)
Si se desea que al ensamblar el programa, el MCU disponga de valores iniciales en su memoria
EEPROM, se usa la directiva #ROM, indicando la dirección en la que se desea disponer de un dato,
teniendo en cuenta que la primer posición de EEPROM se direcciona 0x2100.
Ej:
#ROM INT8 0x2100={0X10} // 10h en dirección 00
#ROM INT8 0x2101={127} // 127d en dirección 01
#ROM INT8 0x2102={“A”} // ‘A’ ASCII en dirección 02
COMUNICACIÓN SERIAL POR USART (Universal Synchronous Asynchronous Receiver Transmitter):
A nivel lógico, USART es compatible con RS‐232.
Pasos para realizar comunicación serial:
• Se definen los parámetros de comunicación (Identificador, Tasa de Baudios, Pin de Transmisión,
Pin de Recepción, Paridad, Timeout).
Ej: 2400 bps, Tx: RC6, Rx: RC7, Sin Paridad, sin Timeout:
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7)
• Para transmitir un dato se usa la instrucción Putc(Dato), para transmitir una cadena de texto se
usa Printf(“Texto”).
• Para recibir un dato, primero se averigua si hay un dato disponible mediante la instrucción
Kbhit(), que toma el valor true en ese caso. Luego, mediante Getc() o Gets() se lee un dato o una
cadena de texto respectivamente.
EJEMPLO:
Realizar un programa para un 16F877A que transmita por puerto serie cada 20ms el contenido de la
variable ‘DatoTx’. Adicionalmente, el programa debe verificar de forma continua si se recibe un
dato por puerto serie y en caso de que así sea, éste debe almacenarse en la variable ‘DatoRx’.
/////////////////////////////////////////////////////////////////////////////////////////
// //
// EJEMPLO DE TRANSMISIÓN Y RECEPCIÓN SERIAL //
// //
/////////////////////////////////////////////////////////////////////////////////////////
#include <16F877A.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7) // Se definen los parámetros de comunicación
int8 DatoTx, DatoRx;
// PROGRAMA PRINCIPAL
void main() {
while (true){
delay_ms(20);
putc(DatoTx); // Transmisión de DatoTx
if(kbhit()){ // Preguntamos si hay disponible dato en bufer de recepción.
DatoRx= getc(); // Si así es, lo leemos y guardamos en DatoRx.
}
}
}
INTERRUPCIONES
Los diferentes recursos y periféricos en cualquier MCU, se pueden manejar normalmente por
interrupción. En este caso, se ordena al procesador iniciar el trabajo con un módulo dado y, cuando
éste termine saltará a ejecutar una rutina preestablecida.
Para el caso de los MCU’s de la serie 16F87X, la arquitectura de interrupciones está dada por:
Cuando un módulo termina su labor, se encarga de levantar la bandera de interrupción ‘XXIF’, sin
embargo, esto no garantiza que se ejecute una rutina de atención a interrupción. Para que se
genere la interrupción, es necesario que la bandera de habilitación de interrupción del módulo
(XXIE) esté activa y, que la bandera de habilitación global (GIE) esté activa y para los periféricos
además, ‘PEIE’ debe estar activa.
La manera más general de trabajar en el compilador cualquier recurso o periférico, corresponde
con el siguiente esquema:
SETUP_MODULO(Parámetros) : Permite configurar un módulo del MCU.
Los nombres de estos parámetros están disponibles en
el archivo .h de cada procesador.
SET_MODULO(valor) : Ajusta valor específico del módulo.
CLEAR_INTERRUPT(MODULO) : Borra flag de interrupción del módulo (XXIF).
ENABLE_INTERRUPTS(MODULO) : Habilita Interrupción del módulo (XXIE).
DESABLE_INTERRUPTS(MODULO) : Deshabilita Interrupción del módulo (XXIE).
ENABLE_INTERRUPTS(GLOBAL) : Habilita Interrupciones globales (GIE, PEIE).
RUTINA ATENCIÓN A INTERRUPCIÓN (ISR):
#INT_MODULO
void NombreDeseado(){
Sentencias;
}
En algunos casos se requiere una directiva de tipo:
#USE MODULO(Parámetros)
MÓDULOS TEMPORIZADORES/CONTADORES:
Es común en los MCU´s encontrar módulos que se encargan de llevar de forma autónoma un
conteo de ciclos máquina o, de pulsos que entran por ciertos pines específicos. A estos módulos se
les denomina Timers.
Para el caso de la familia 16F87XA, se cuenta con tres Timers (Timer0 o RTCC, Timer1 y Timer2).
Una descripción resumida de los tres Timers se presenta en la tabla siguiente:
TIMER0 TIMER1 TIMER2
• Temp/contador de 8 bits.
• Flanco externo Temporizador/contador de 16
Temporizador de 8 bits
seleccionable en modo bits.
contador.
Pin de entrada de pulsos: RA4 Pin de entrada de pulsos: RC0 Periodo programable (0 a 255)
Modos de trabajo: Modos Básicos de trabajo: Post‐escalador (post‐divisor)
RTCC_INTERNAL T1_DISABLED de la frecuencia de reloj
RTCC_EXT_L_TO_H T1_INTERNAL programable:
RTCC_EXT_H_TO_L T1_EXTERNAL Cualquier número entre 0 y 16
Prescalador (predivisor) de la Prescalador (predivisor) de la Prescalador (predivisor) de la
frecuencia de reloj frecuencia de reloj frecuencia de reloj
programable: programable: programable:
RTCC_DIV_2, RTCC_DIV_4, T1_DIV_BY_1 T2_DISABLED
RTCC_DIV_8, RTCC_DIV_16, T1_DIV_BY_2 T2_DIV_BY_1
RTCC_DIV_32, RTCC_DIV_64, T1_DIV_BY_4 T2_DIV_BY_4
RTCC_DIV_128, RTCC_DIV_256 T1_DIV_BY_8 T2_DIV_BY_16
Funcionamiento General:
Funcionamiento General: Funcionamiento General: El valor del Timer se
El valor del Timer se El valor del Timer se incrementa cada ‘predivisor’
incrementa cada ‘predivisor’ incrementa cada ‘predivisor’ pulsos externos o CM. El
pulsos externos o CM. pulsos externos o CM. conteo se reinicia cada
´periodo+1’ incrementos.
Genera interrupción si el valor Genera interrupción al pasar Genera interrupción cada
del Timer pasa de 0xFF a 0x00 de 0xFFFF a 0x0000 ‘postdivisor’ conteos.
Nombre Interrupción: Nombre Interrupción: Nombre Interrupción:
INT_TIMER0 INT_TIMER1 INT_TIMER2
Número de Pulsos o Ciclos Número de Pulsos o Ciclos
Si el valor inicial del Timer es 0:
Máquina necesarios para Máquina necesarios para
generar interrupción: generar interrupción:
N= (Periodo+1)*Prediv*PosDiv
N= (256‐ValIniTimer)*Prediv N= (65536‐ValIniTimer)*Prediv
Ejercicios:
1. (a) Cuál es el mayor número de pulsos externos o ciclos máquina que podría contabilizarse de
forma directa con cada Timer. (b) Con cristal de 4Mhz, a cuánto tiempo equivalen los resultados
anteriores.
2. Si se desea implementar una temporización de 50ms y se dispone de un cristal de 4MHz, cuáles
serían los valores de configuración para cada Timer (Prediv, Valor Inicial, Postdiv, Periodo).
Pasos para realizar una temporización con Timer:
1. Realizar los cálculos de los valores de configuración, como se hizo en el ejercicio pasado.
2. Configurar el módulo:
setup_timer_0(Modo|Predivisor) ‐ setup_timer_1(Modo|Predivisor)
setup_timer_2(Predivisor,Periodo,PosDivisor)
3. Inicializar el valor del Timer:
set_timer0(ValorInicialTimer) ‐ set_timer1(ValorInicialTimer)
set_timer2(0)
4. Borrar la bandera de interrupción del módulo:
clear_interrupt(INT_TIMERx)
5. Si se desea, se habilita interrupción:
enable_interrupts(INT_TIMER0) enable_interrupts(GLOBAL)
6. En caso de trabajar por interrupción, en la rutina de atención a interrupción se hace el segmento de
programa que se desea ejecutar cuando el módulo termina la temporización. Adicionalmente, aquí
es necesario volver a asignar el valor inicial del Timer, en caso de que éste sea diferente de cero.
7. En cualquier momento se puede obtener el valor de un Timer, mediante la instrucción get_timerX().
Ej. Val= get_timer1()
EJEMPLO:
Atender el timer0 por interrupción, cada 1 ms en un PIC 16F877A con XT= 4MHz.
// Encabezado
...
// Otras funciones
...
#INT_TIMER0
void INTERRUPCION_1ms(){ // Atención a interrupción cada 1 ms
SET_TIMER0(6);
// Atención a interrupción
}
void main(){
...
// Sentencias;
...
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_4);
SET_TIMER0(6);
clear_interrupt(INT_TIMER0);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(true){
Sentencias;
}
}