mplab c30 y pic24

57
2010 Alejandro www.infopic.comlu.com 10/04/2010 MPLAB C30 y PIC24

Upload: alejandro-c

Post on 22-Mar-2016

224 views

Category:

Documents


1 download

DESCRIPTION

Aquí se presenta una introducción para comenzar a trabajar con los microcontroladores PIC24 y MPLAB C30.

TRANSCRIPT

Page 1: MPLAB C30 y PIC24

2010

Alejandro

www.infopic.comlu.com

10/04/2010

MPLAB C30 y PIC24

Page 2: MPLAB C30 y PIC24

2

Tabla de contenido PIC24 Y C30 ............................................................................................ 3

Programación. ...................................................................................... 5

Introducción: ........................................................................................ 6

Memoria de datos. ............................................................................... 9

Creando el primer Proyecto. ..................................................................... 11

El Código Fuente ................................................................................... 13

La función principal. ........................................................................... 14

Operación de cambio de clock. ................................................................. 17

Ejemplo en C30: ................................................................................ 18

Algunos aspectos de C30: ........................................................................ 19

Atributos de variables. ......................................................................... 20

Atributos a funciones: .......................................................................... 20

Interrupciones. ..................................................................................... 21

Registros de control y estado de las interrupciones: ...................................... 22

Manejo de las interrupciones: ................................................................ 23

Módulos temporizadores. ......................................................................... 25

Modos de operación. ........................................................................... 26

Configuracion timer 32-bits. .................................................................. 28

Operación de lectura y escritura. ............................................................ 28

Ejemplo modo temporización, interrupción cada 500ms a 40 MIPS. .................... 29

Selección de pines de los periféricos. .......................................................... 32

UART. ............................................................................................... 34

Baud rate. ........................................................................................ 35

Configuración de la USART. ................................................................... 35

Uso del printf en C30. .......................................................................... 42

Notificación de cambio de estado de pines. ................................................... 43

Control LCD. Modo 4-bits o 3-pines más registro de desplazamiento ...................... 46

Modificación de funciones de bajo nivel de la librería stdio.h para re-direccionar

salida de datos de printf. ...................................................................... 47

ANEXO A. ........................................................................................... 49

Page 3: MPLAB C30 y PIC24

3

PIC24 Y C30

Aquí se presenta una introducción para comenzar a trabajar con los microcontroladores

PIC24 y MPLAB C30.

Para comenzar necesitamos tener instalado:

MPLAB IDE, entorno de programación.

MPLAB C30, C Compiler (Versión estudiantil)

En estos ejemplos voy a trabajar principalmente con el PIC24HJ128GP505 y además

mostraré algunos ejemplos con el PIC24FJ128GA010. El primero, en un primer momento,

lo he elegido sencillamente por dos razones, disponibilidad y porque tiene package DIP que

nos facilita enormemente la construcción de una placa para trabajar con él. Y para ello he

construido una mini placa de desarrollo para estos microcontroladores DIP28.

Page 4: MPLAB C30 y PIC24

4

Características:

Prototipo para todos los 28-pines, DIP PIC24, dsPIC30F y dsPIC33F

Reguladores para 3.3V o 5V de operación sobre la placa

Comunicación UART

Conector para PICkit™ 2 In-circuit Debugger/Programmer

Header para acceder a todos los pines I/O del microcontrolador

Por aquí esta el pdf con el esquemático y el PCB para su construcción.

Para trabajar con el segundo he realizado una placa adaptadora para usar la Multiboard del

amigo Felixls:

Si alguien se anima se comunica por mail para el pdf de la placa….

Algunas características de estos PIC:

PIC24HJ128GP502:

El CPU puede operar hasta 40 MIPS

Oscilador interno de 8Mhz

PLL interno

Memoria de programa de 128kB

Memoria RAM 8kB

Direccionamiento lineal de memoria de programa de hasta 4MB

Direccionamiento lineal de la memoria de datos de hasta 64kB.

Stack por software

Hardware para multiplicación 16x16, división 32x16 y 16x16.

8 canales DMA (Acceso a memoria directo)

2 UART – 2 SPI – 1 I2C

Page 5: MPLAB C30 y PIC24

5

Módulo AD. 10-bit 1.1 Msps o 12-bit a 500ksps.

4 Módulos Comparación/Captura/PWM. 16-bits PWM.

Timers, 5 x 16-bit 2 x 32-bit.

RTC (Reloj de tiempo real)

PPS (Periferal Pin Select).

Etc.

PIC24FJ128GA010:

El CPU puede operar hasta 16 MIPS

Oscilador interno de 8Mhz

PLL interno

Memoria de programa de 128kB

Memoria RAM 8kB

Direccionamiento lineal de memoria de programa de hasta 4MB

Direccionamiento lineal de la memoria de datos de hasta 64kB.

Stack por software

Hardware para multiplicación 16x16, división 32x16 y 16x16.

2 UART – 2 SPI – 2 I2C

Puerto paralelo Master-Slave. Direccionamiento 16-bits, Datos 8-bits o 16-bits.

Módulo AD. 10-bit a 500ksps.

5 Módulos Comparación/Captura/PWM. 16-bits PWM.

Timers, 5 x 16-bit 2 x 32-bit.

RTC (Reloj de tiempo real)

Módulo CRC configurable.

5 interrupciones externas.

Pines configurables como a colector abierto (Salidas)

Etc.

Sencillamente espectacular!!!

Algo que me llama la atención es el DMA, leyendo el datasheet ya nos damos una idea de

que se trata:

Acceso directo a memoria (DMA) es un mecanismo muy eficiente de copia de datos entre

periféricos SFRs (por ejemplo, registro de recepción UART, buffer de entrada Captura 1), y

buffer o variables almacenadas en la memoria RAM, con una intervención mínima del CPU.

El controlador DMA puede copiar automáticamente bloques enteros de datos sin necesidad

de software para leer o escribir en lo SFRs cada vez que se produce una interrupción del

periférico. El controlador DMA usa un bus dedicado para la transferencia de datos y por lo

tanto, no utiliza ciclos de ejecución del código del CPU.

Programación. Para programarlos hay diversas herramientas, entre las cuales encontramos las de Microchip

(PICKit2, PicKit3, ICD2, ICD3, ect) , GTP-USB [plus], etc. En mi caso he construido el

Page 6: MPLAB C30 y PIC24

6

PICKit2 Clone, que junto a Felixls hemos diseñado uno compatible con estos

microcontroladores que trabajan a 3.3V. Por aquí tienen una de las versiones disponibles por

el momento.

Introducción:

Microchip ofrece 2 familias de microcontroladores de 16 bits, la familia PIC24F de bajo

costo y performance media (Muy rentable, máximo 16 MIPS) y la familia PIC24H de alta

performance (Máximo 40 MIPS). Entre las familias hay muchos atributos en común, entre

ellos son la compatibilidad de pinout, compatibilidad de periféricos, misma herramienta de

desarrollo, entre otras.

Principales características:

• Memoria de programa flash de 4 a 256 Kbytes.

• Memoria RAM de 0.5 a 16Kbytes.

• DMA, 8 canales con 2Kbytes de RAM.

• Package de 14 a 100 pines.

• Peripheral Pin Select (PPS), selección de pines para periféricos de forma flexible.

• Arquitectura diseñada para las exigencias de control en tiempo real.

• Osciladores integrados de alta velocidad y baja potencia con PLL.

• USB-OTG en productos de 28 a 100 pin. (Soporta USB HOST).

• UART, soporta LIN, IrDa, RS-232, RS485 con 4 niveles de buffer FIFO o DMA.

• SPI, 8 niveles de Buffer FIFO o DMA.

• I2C, modo multi-master, slave de 7/10 bits de direccionamiento.

• Bus CAN, con 8 buffers de transmisión y 32 de recepción.

• CRC, programable.

• Temporizadores de 16-bits que se pueden colocar en cascada para formar de 32-bits.

• Reloj de Tiempo Real con Calendario.

• A/D de 10/12-bits.

Page 7: MPLAB C30 y PIC24

7

• Unidad de medida de tiempo de carga (CTMU), una fuente de corriente constante

acoplada al ADC que provee la habilidad de medir capacidades o tiempos con una

resolución de ns. Útil para realizar teclados sensibles al tacto.

Page 8: MPLAB C30 y PIC24

8

La CPU de los PIC24 posee una arquitectura Harvard modificada con avanzado set de

instrucciones con palabras de 24-bits de ancho. La memoria de programa tiene 4M-24bits

como espacio de direcciones, pero solo se implementa un porcentaje que dependerá del

dispositivo.

Las instrucciones se ejecutan en un solo ciclo de programa salvo algunas excepciones como

las instrucciones que cambian el flujo del programa, instrucciones de tabla e instrucciones de

acceso a PSV que pueden ocupar más.

Estos microcontroladores poseen 16 registros W de 16 bits, en donde una palabra puede ser

un dato o una dirección. El último registro W (W15) opera como puntero de software de la

memoria de programa para llamadas a subrutinas y pedidos de interrupción

Una propiedad que agrega es la capacidad de que los últimos 32KB de la memoria de datos

pueden ser mapeados en la memoria de programa cómo 16Kword, quedando definida el área

por el registro Program Space Visibility Page (PSVPAG). De esta manera los datos

mapeados en la memoria de programa son accedidos como si fueran de la memoria de datos

y no necesitan de instrucciones especiales para su acceso (por ejemplo, TBLRD, TBLWT).

Espacio de memoria DMA (Solo para los PIC24H), para transferencia de datos desde

periféricos. El controlador DMA usa un bus dedicado para la transferencia de datos y por lo

tanto, no utiliza ciclos de ejecución del código del CPU.

Posee un bloque de multiplicación 17x17 que puede realizar operaciones signadas, sin signo

o mixtas de 16-bits x 16bits o 8-bits x 8-bits en un solo ciclo de programa. Además también

hay un bloque de división de 32-bits o 16-bits dividido 16-bits signado o no signado, que

requiere 19 ciclos de programa.

Memoria de programa.

Como dijimos anteriormente estos dispositivos tienen 4M-24bits como espacio de

direcciones de memoria de programa. Este espacio es direccionable/accedido por 24 bits

derivados de tres métodos, el contador de programa (PC, 23 bits, se limita de 0x000000 a

0x7FFFFF), la tabla de operación (TBLRD, TBLWT) o desde el re-mapeo del espacio de

memoria.

Ésta es dividida en 2 sectores, en el espacio de programa de usuario y en el espacio de

Page 9: MPLAB C30 y PIC24

9

configuraciones. El espacio de programa de usuario (0x000000 a 0x7FFFFF) a la vez está

divido en sectores:

• Vector de reset. (0x000000 a 0x000003)

• Tabla de vectores de interrupciones. (0x000006 a 0x0000FE)

• Tabla alterna de vectores de interrupciones. (0x000106 a 0x0001FE)

• Memoria de programa. (0x000200 a 0x…., dependerá del dispositivo, máximo

0x7FFFFF)

Memoria de datos.

Como estos dispositivos son de arquitectura Hardvard tienen buses independientes para la

memoria de datos y para la memoria de programa, lo que permite el acceso simultaneo. La

memoria de datos de los PIC24 tiene 16 bits de ancho y su direccionamiento es lineal. El

espacio de datos es accedido por dos unidades generadoras de direcciones (Address

Generation Units o AUGs) para operaciones de lectura y escritura.

Un bloque de direccionamiento efectivo (Effective Address o EA) es responsable del

direccionamiento de la memoria de datos. En la mitad baja del espacio de memoria

Page 10: MPLAB C30 y PIC24

10

(EA<15>=0) se implementa la memoria RAM (0000h a 7FFFh), mientras que la mitad alta

es reservado para el área de visualización de la memoria de programa (PSV).

Espacio SFR

Los primeros 2 Kbytes del espacio de memoria es ocupado por los registros de funciones

especiales, estos son usados para el control de operación del CPU y de los periféricos del

mismo. Memoria RAM DMA.

Los PIC24H disponen de un espacio de memoria con doble puerto que puede ser accedido

simultáneamente por el CPU o por el módulo DMA. Su capacidad depende del dispositivo.

Este espacio de memoria es utilizado por el controlador DMA para transferir datos desde

periféricos que lo soporten, sin utilizar ciclos adicionales del CPU.

Además este espacio de memoria puede usarse para propósitos generales si no se utiliza la

función DMA en el proyecto.

Page 11: MPLAB C30 y PIC24

11

Creando el primer Proyecto. Creamos un nuevo proyecto:

Seleccionamos dispositivo:

Page 12: MPLAB C30 y PIC24

12

Creamos y guardamos archivo fuente del proyecto con extensión c, incluyéndolo al mismo:

Insertamos el “linker Script” (*.GLD) del dispositivo utilizado.

Page 13: MPLAB C30 y PIC24

13

Este archivo informa al enlazador acerca de posiciones predefinidas en la memoria (de

acuerdo a la hoja de datos del dispositivo), así como proporciona información esencial

espacio de memoria, tales como: cantidad total de memoria Flash disponibles, la cantidad

total de memoria RAM disponible, y sus rangos de direcciones. Se encuentra en

\Microchip\MPLAB C30\support\PIC24H\gld.

Con esto, creo que ya estamos listos para programar algo en C30

El Código Fuente

En primer lugar incluimos el archivo *.h del dispositivo utilizado, este contiene definidos los

registros funciones especiales y bits de los mismos.

#include <p24hj128gp502.h>

Ahora sigue la configuración de fuses, en este caso depende del dispositivo utilizado y para

saber que funciones y como configurarlas, debemos ir a la sección de características

especiales en el datashhet.

Para este dispositivo tenemos:

o Bits de configuración para protección de Flash y RAM:

_FBS(x)

_FGS(x)

o Bits de configuración para selección de oscilador.

_FOSCSEL(x)

_FOSC(x)

o Bits de configuración para selección de Watchdog.

_FWDT(x)

o Configuraciones generales como Power-on Reset, pin I2C, JTAG, ect.

_FPOR(x)

_FICD(x)

Page 14: MPLAB C30 y PIC24

14

Para saber la sintaxis hay que darle una miradita al *.h del dispositivo, al final están los

define de los mismos.

Por ejemplo:

_FOSCSEL(FNOSC_PRIPLL); // Oscilador primario + PLL

_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock y

Fail-Safe Clock des-habilitados, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

La función principal.

main(void){

}

Función que debe estar si o si, pues es la primera que se ejecuta al iniciar es sistema, en

donde configuraremos los periféricos y llevaremos control de nuestra aplicación.-

Configuración del PLL y determinación de MIPS de trabajo.

El oscilador primario y el oscilador interno (FRC), puede utilizar el PLL interno para

obtener mayores velocidades de operación, además proporciona una significativa

flexibilidad en la selección de la velocidad de funcionamiento del dispositivo.

La salida del oscilador primario o FRC, el cual denominamos 'Fin', se divide por un factor

de pre-escale (N1), de 2, 3, ... o 33 antes de ingresar al PLL de tal manera que esté entre el

intervalo de 0,8 MHz a 8 MHz. El factor de pre-escaler ‘N1’ se selecciona mediante los bits

PLLPRE <4:0> del registro CLKDIV.

Page 15: MPLAB C30 y PIC24

15

El divisor de realimentación del PLL, es seleccionado mediante los bits PLLDIV <8:0> del

registro PLLFBD, que proporciona un factor ‘M‘ (2…513), por el que se multiplica la

entrada al PLL. Este factor debe ser elegido de modo que la frecuencia resultante de salida

esté en la gama de 100 MHz a 200 MHz.

La salida del PLL se divide por un factor post-escaler ‘N2’. Este factor se selecciona

mediante el los bits PLLPOST <7:6> del registro CLKDIV. ‘N2’ puede ser 2, 4 u 8, y

deberán seleccionarse de forma que la frecuencia de salida PLL (FOSC) esté en el rango de

12,5 MHz a 80 MHz, lo que genera una velocidad de funcionamiento del dispositivo de

6.25-40 MIPS.

Fosc=Fin*(M/(N1*N2))

Tenemos que Fcy=Fosc/2, pero además con los bits DOZE<14:12> del registro CLKDIV

podemos aplicar un pos-escaler entre 2,4,8,16,32,64 o 128.

Por ejemplo con un cristal de 4MHz, N1 = 2, M = 80; N2 = 2; Fcy = Fosc/2 obtenemos 40

MIPS.

PLLFBD = 0x0000;

CLKDIV = 0x004E;

Bueno, hasta aquí ya hemos definido algunos detalles de como va a trabajar nuestro

microcontrolador, ahora hagamos un ejemplo sencillo de hacer titilar los led ubicados en

RB12-RB15.

La librería para agregar demoras a nuestro programa libpic30.h, pero tiene un bug, no se

puede usar a 40MIPs Para utilizar demoras se debe incluir la librería, definir la

frecuencia FCY y disponemos de la función __delay32(Numero de ciclos) con la cual

podemos hacer demoras mayores a 12 ciclos. Luego tenemos definidos 2 macros:

#if !defined(FCY)

extern void __delay_ms(unsigned long);

extern void __delay_us(unsigned long);

#else

#define __delay_ms(d) \

{ __delay32( (unsigned long) (d)*(FCY)/1000); }

#define __delay_us(d) \

{ __delay32( (unsigned long) (d)*(FCY)/1000000); }

#endif

El problema a 40MIPs surge en la definición del macro, pues d*FCY se va de rango de los

32 bits cuando d es mayor a 107 aprox. y genera una demora incorrecta, para solucionarlo

hay que cambiar a unsigned long long para trabajar con 64-bits. También podemos agregar

una demora de segundos.

#define _delay_ms(d) __delay32( (unsigned long long) (d)*(FCY)/1000)

#define _delay_us(d) __delay32( (unsigned long long) (d)*(FCY)/1000000)

#define _delay_s(d) __delay32( (unsigned long) (d)*(FCY))

Page 16: MPLAB C30 y PIC24

16

Ejemplo:

/* ********* Primer Proyecto en C30 *************

\Autor: Suky

\Fecha: 11/01/10

*/

#include <p24hj128gp502.h>

#define FCY 40000000UL

#include <libpic30.h>

_FOSCSEL(IESO_OFF & FNOSC_PRIPLL); // Oscilador primario + PLL

_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock y

Fail-Safe Clock des-habilitados, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

main(int argc, char * argv[]){

// Cristal de 4MHz y PLL trabajando a 40 MIPS.

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

while(_LOCK==0);

AD1PCFGL=0xFFFF; // todos los pines digitales.-

TRISB=0x0FFF; // Configuramos RB2-RB15 como salidas.-

while(1){

LATB=0xF000;

__delay_ms(500);

//__delay_s(1);

LATB=0x0000;

__delay_ms(500);

//__delay_s(1);

}

}

Utilizando el PIC24FJ128GA010:

#include <p24fj128ga010.h>

_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & BKBUG_OFF & COE_OFF & ICS_PGx2

& FWDTEN_OFF & WINDIS_OFF & FWPSA_PR128 & WDTPS_PS32768);

_CONFIG2( FNOSC_PRI & FCKSM_CSDCMD & POSCMOD_HS );

#define FCY 10000000UL

#include <libpic30.h>

int main (int argc, char * argv[]){

TRISF=0xFFF3;

LATFbits.LATF2=0;

LATFbits.LATF3=0;

while(1){

LATFbits.LATF2=0;

__delay_ms(500);

LATFbits.LATF2=1;

__delay_ms(500);

}

}

Page 17: MPLAB C30 y PIC24

17

Operación de cambio de clock.

En esta arquitectura se puede cambiar entre 4 fuentes de reloj (Primario, LP, FRC y LPRC)

por medio de software cuando se desee. El primario tiene 3 sub modos de operación (XT,

HS y EC) que son configurados mediante los bits de configuración, ósea que en tiempo de

operación no se puede cambiar de modo, pero si se puede cambiar desde o hacia la fuente

primaria.

Para habilitar el cambio de clock, el bit de configuración FCKSM1 del registro de

configuración debe ser programado como 0.

Los bits COSC (OSCCON<14:12>) reflejan la selección de la fuente de clock de los bits

de configuración FNOSC, y los bits de control NOSC (OSCCON<10:8>) controlan la

selección de clock cuando el cambio está habilitado. El bit de control OSWEN

(OSCCON<0>) inicia el cambio y muestra la finalización, pero si el cambio no esta

habilitado, este bit siempre se sostiene a 0.

Secuencia de cambio de oscilador.

El cambio de clock requiere la siguiente secuencia:

1. Si se desea, para determinar la fuente de clock actual se debe leer los bits COSC

(OSCCON<14:12>).

2. Realizar la secuencia de desbloqueo para permitir la escritura del byte alto del registro

OSCCON.

3. Escribir el valor apropiado a los bits de control NOSC (OSCCON<10:8>) para la nueva

fuente de clock.

4. Realizar la secuencia de desbloqueo para permitir la escritura del byte bajo del registro

OSCCON.

5. Setear el bit OSWEN (OSCON<0>) para iniciar el cambio de oscilador.

Una vez que esta secuencia ha sido completada, el hardware responderá de la siguiente

manera:

1. Se compara los bits COSC con los bits NOSC, si son iguales el bit OSWEN es borrado

automáticamente y el cambio de clock es abortado.

2. Si el cambio de clock es valido, los bits LOCK y CF del registro OSCCON son

borrados.

3. El nuevo oscilador es encendido por el hardware y se espera el tiempo adecuado. Si

usará PLL, se espera hasta que el PLL se enganche. (LOCK=1)

4. El hardware espera 10 ciclos de clock de la nueva fuente y recién realiza el cambio.

5. Se borra el bit OSWEN para indicar la finalización de la transición, y los bits COSC se

actualizan con los de NOSC.

6. La fuente antigua de clock es apagada.

Page 18: MPLAB C30 y PIC24

18

Ejemplo en C30:

Pasando de oscilador principal XT de 4MHz sin PLL (2 MIPS) a oscilador principal XT con

PLL (40 MIPS).

/* ********* Cambiando clock *************

\Autor: Suky

\Fecha: 07/01/10

*/

#include <p24hj128gp502.h>

_FOSCSEL(IESO_OFF & FNOSC_PRI); // Oscilador primario

_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock

habilitado, Fail-Safe Clock des-habilitado, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

void Delay_us(unsigned int time){

while(time!=0){

asm volatile("repeat #34");

asm volatile("nop");

time--;

}

}

void Delay_ms(unsigned int time){

while (time!=0){ Delay_us(1000); time--;}

}

main(void){

// Configuramos PLL para trabajar a 40 MIPS con Cristal de 4MHz.-

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

// Cambiamos clock para incroporar PLL.-

__builtin_write_OSCCONH(0b011); // Indicamos cambio a clock

primario con PLL.-

Page 19: MPLAB C30 y PIC24

19

__builtin_write_OSCCONL(0x01); // Iniciamos cambio.-

while(OSCCONbits.OSWEN == 1) {}; // Esperamos a que se termine el

cambio de clock.

AD1PCFGL=0xFFFF; // todos los pines digitales.-

TRISB=0x0FFF; // Configuramos RB2-RB15 como salidas.-

while(1){

LATB=0xF000;

Delay_ms(1000);

LATB=0x0000;

Delay_ms(1000);

}

}

Otra característica interesante que tienen estos microcontroladores es el Fail-Safe Clock

Monitor, el cual si está habilitado permite que el dispositivo continúe operando ante la falla

de un oscilador. Al estar habilitado el oscilador interno LPRC esta funcionando durante

todo el tiempo excepto en modo Sleep, y cuando el oscilador principal falla se genera un

traps (Interrupción por falla del sistema) y el sistema cambia el clock al oscilador LPRC. El

software del usuario será el encargado de intentar reiniciar el oscilador que ha fallado.

Algunos aspectos de C30: Tipos de variables:

Variables enteras:

Variables flotantes:

Page 20: MPLAB C30 y PIC24

20

Atributos de variables.

En C30 para fijar atributos especiales a las variables se utiliza la palabra __attribute__ la

cual es seguida por los atributos dentro de paréntesis dobles. Los atributos pueden ser:

• address

• aligned

• deprecated

• far

• mode

• near

• noload

• packed

• persistent

• reverse

• section

• sfr

• space

• transparent_union

• unordered

• unused

• weak

Ejemplos:

Comparando con C18 podemos ver la diferente forma de sintaxis:

C18:

near int Data;

far int Data;

rom int Buffer[6]={0x01,0x0A,0x07,0xC1,0x26,0xAA};

#pragma udata data=0x400

volatile unsigned int Buffer[100];

C30

__attribute__((near)) int Data

__attribute__((far)) int Data

__attribute__ ((section(“.romdata”), space(prog))) int

Buffer[6]={0x01,0x0A,0x07,0xC1,0x26,0xAA};

volatile unsigned int __attribute__((address(0x400))) Buffer[100];

Atributos a funciones:

También se utiliza la palabra clave __attribute__ para dar una propiedad a una función, los

soportados son:

• address (addr)

• alias ("target")

• const

• deprecated

• far

Page 21: MPLAB C30 y PIC24

21

• format (archetype, string-index, first-to-check)

• format_arg (string-index)

• interrupt [ ( [ save(list) ] [, irq(irqid) ] [, altirq(altirqid)] [,preprologue(asm) ] ) ]

• near

• no_instrument_function

• noload

• noreturn

• section ("section-name")

• shadow

• unused

• weak

Ejemplos:

// La function se ubica en la dirección 0x100.-

void Funcion(int a, char b) __attribute__ ((address(0x100))) {

...

}

// La función es ubicada en la sección libtext pero es definida en otro

archivo fuente.-

extern void Funcion(void) __attribute__ ((section (".libtext")));

// Asigna a la function como tratamiento de interrupción.-

void __attribute__((__interrupt__)) isr0(void);

// Se indica que se guarden las variables automáticamente al ejecutarse la

interrupción.-

void __attribute__((__interrupt__(__save__(var1,var2)))) isr0(void);

// Se indica que se use el salvado de contexto rápido .-

void __attribute__((__interrupt__, __shadow__)) isr0(void);

Para más información leer C30 User guide.

Interrupciones.

La arquitectura de los PIC24 proporciona 118 fuentes de interrupción las cuales poseen un

vector propio para cada una de ellas, a diferencia de la familia 16F que tiene solo un vector,

y la familia 18F la cual posee 2 vectores para baja y alta prioridad. En este caso no solo

existen 2 niveles de prioridad, sino 8 que pueden asignarse a cada evento. Además de las

interrupciones tenemos 8 TRAPs, que son eventos prioritarios (Sin niveles de prioridad) de

falla del sistema.

Los vectores están agrupados en dos tablas, la principal (IVT-Interrupt Vector Table) y

alternativa (AIVT-Alternate Interrupt Vector Table) la cual es para soporte de debbug.

La IVT reside en la memoria del programa comenzando en la dirección 0x000004, la cual

contiene 126 vectores, 8 Traps y 118 fuentes de interrupción. Cada vector de interrupción

tiene asociado 24-bits para indicar la dirección de arranque de la rutina de servicio de

interrupción individual asociada (ISR).

Las prioridades están definidas por su prioridad natural, ósea está asociada a su posición en

la tabla de vectores. Las de más baja dirección tienen una prioridad natural más alta.

La AIVT está localizada inmediatamente después la de IVT como se muestra en la siguiente

figura y los vectores están organizados de la misma manera. El acceso esta controlado por el

Page 22: MPLAB C30 y PIC24

22

bit ALTIVT (INTCON2 <15>), el cual si está en alto todas las interrupciones y

excepciones son procesadas en éste.

Registros de control y estado de las interrupciones:

Para estas funciones se utilizan 30 registros:

• INTCON1

• INTCON2

• IFSx

• IECx

• IPCx

• INTTREG

INTCON1 AND INTCON2

Contienen los flags para control de estado de los traps, selección de vector de interrupciones

AIVT, etc.

IFSx

Estos registros tienen todos los flags de las peticiones de interrupción. Cada fuente de

interrupción tiene su bit de estado, que es seteado por el periférico respectivo y es borrado

mediante software.

IECx

Page 23: MPLAB C30 y PIC24

23

Estos registros tienen los bit habilitadores/des-habilitadores de cada fuente de interrupción.

IPCx

Estos registros asignan a cada fuente de interrupción un nivel de prioridad entre 8

disponibles.

INTTREG

Este registro contiene el numero del vector de interrupción asociado y el nuevo nivel de

prioridad de interrupción del CPU (VECNUM<6:0> y IRL<3:0>). El nuevo nivel de

prioridad de interrupción es la prioridad de la interrupción pendiente.

STATUS/CONTROL REGISTERS

Aunque no son específicamente parte del hardware de control de interrupción, los registros

de control del CPU contienen bit que controlan la funcionalidad de las interrupciones.

El registro SR contiene los bits IPL<2:0> que indican el actual nivel de prioridad de

del CPU. El CPU tiene 16 niveles de prioridad (0-15), y una fuente de interrupción o

una Traps debe tener mayor prioridad para que pueda generar una excepción en el

proceso.

El registro CORCOn contiene el bit IPL3, que junto a IPL<2:0> indican el nivel

actual de prioridad del CPU. Este bit es solo de lectura para que los eventos traps

(Niveles de prioridad de 8 a 15) no puedan ser enmascarados por el software del

usuario.

Manejo de las interrupciones:

Inicialización.

1. Setear el bit NSTDIS si no se desea interrupciones anidadas.

2. Seleccionar el nivel de prioridad para las fuentes de interrupción escribiendo el bit de

control en el regsitro IPCx apropiado. El nivel de prioridad depende de la aplicación

específicamente y el tipo de fuente de interrupción. Si no se desea niveles de prioridad

múltiples, los bits de control de los registros IPCx para todas las fuentes de interrupción

habilitadas pueden ser programadas al mismo valor distinto de cero.

3. Borrar la bandera de interrupción asociada en el registro IFSx.

4. Habilitar la fuente de interrupción seteando el bit de control asociado en el registro

apropiado IECx.

Nota: Interrupción anidada se refiere a la posibilidad de que una interrupción de baja

prioridad pueda ser interrumpida por otra de mayor prioridad. Si no esta habilitada, se espera

a que termine el tratamiento de una para atender a la siguiente.

Rutina de servicio de interrupción en C30.

Las rutinas de interrupción (ISR) son como cualquier otra función que utilizan variables

locales o globales pero que no tienen parámetros de entrada, no retornan valores y no pueden

ser llamadas como otras funciones. Las ISR solo sirven para invocarse a través de una

interrupción por hardware o traps. Para dar por terminada la ISR se utiliza la instrucción (en

assembler, oculta para el usuario de C30) RETFIE la cual devuelve el valor salvado a PC, y

restablece el nivel anterior de prioridad del CPU.

Page 24: MPLAB C30 y PIC24

24

Una recomendación es no llamar a otras funciones dentro de la ISR por problemas de

latencia.

Para declarar una función como una ISR, se le debe asignar el atributo interrupt, siendo la

sintaxis la siguiente:

__attribute__((interrupt [( [ save(ListaVariables)] [, irq(irqid)] [,

altirq(altirqid)] [, preprologue(asm)])]))

void __attribute__((__interrupt__)) _StackError(void){

}

void __attribute__((__interrupt__)) _AltStackError(void){

}

El parámetro opcional save se utiliza para guardar y restablecer una o más variables al entrar

y salir de la ISR.

void __attribute__((__interrupt__(__save__(var1,var2))))isr0(void);

El parámetro opcional IRQ permite colocar un vector de interrupción (IVT) a una

interrupción específica, y el opcional ALTIRQ permite colocar una interrupción a un vector

alterno (AIVT). Cada parámetro requiere un número de identificación. (Más información,

C30 user guide, pag. 96)

void __attribute__((__interrupt__(__irq__(52)))) MyIRQ(void);

void __attribute__((__interrupt__(__altirq__(52)))) MyAltIRQ(void);

El parámetro opcional preprologue permite insertar declaraciones en assembler antes del

código generado por el compilador para la inicialización de la ISR.

Void __attribute__((__interrupt__(__preprologue__("inc _semaphore"))))

isr0(void);

Des-habilitación de interrupciones.

Cada fuente de interrupción tiene su bit de control para habilitarla o des-habilitarla, pero si

desea deshabilitar todas las interrupciones se puede establecer el nivel de prioridad del CPU

mayor al de las interrupciones (máximo nivel 7).

SET_AND_SAVE_CPU_IPL(current_cpu_ipl, 7); // Des-habilita interrupciones.-

RESTORE_CPU_IPL(current_cpu_ipl); // Habilita interrupciones.-

También es posible usar la instrucción DISI que deshabilita las interrupciones (Niveles de

prioridad 1 a 6) durante un determinado número de instrucciones (Máximo 0x3FFF). Esta

instrucción trabaja en conjunto con el registro DISICNT. Si el registro es distinto de cero,

las interrupciones con nivel de prioridad entre 1 a 6 son deshabilitadas, y este registro se

decrementa por cada ciclo. Cuando este registro llega a 0 las interrupciones son re-

habilitadas.

Page 25: MPLAB C30 y PIC24

25

__asm__ volatile ("disi #30"); // Deshabilita interrupción durante 30

ciclos.

__asm__ volatile ("disi #0x1E"); // Deshabilita interrupción durante 30

ciclos.

Módulos temporizadores.

Estos microcontroladores disponen de timers de 16-bits que pueden dividirse en tres tipo de

acuerdo a sus funcionalidades:

Timer tipo A (Timer1)

Timer tipo B (Timer2, 4, 6 y 8 )

Timer tipo C (Timer3, 5, 7 y 9)

Los timers de tipo A, a diferencia de los demás, pueden operar desde un oscilador de baja

potencia de 32kHz y poseen el modo de contador asincrónico desde fuente de clock externa.

En cambio los timers de tipo B y C pueden combinarse para formar timer de 32-bits.

Además los timers de tipo C pueden activar la conversión AD.

Cada modulo timers es un contador/temporizador de 16-bits que tienen los siguientes

registros de control leibles/escribibles:

• TMRx: Registro de 16-bits que lleva la cuenta del timer.

• PRx: Registro de 16-bits que fija periodo al timer.

• TxCON: Registro de control del timer.

También cada modulo tiene asociado sus bits para el control de interrupción, para

habilitación (TxIE), para control de estado (TxIF) y para fijar prioridad (TxIP).

Esquema Timer tipo A:

Esquema Timer tipo B:

Page 26: MPLAB C30 y PIC24

26

Esquema Timer tipo C:

Modos de operación.

Los timers pueden trabajar de los siguientes modos:

• Modo temporizador

• Modo temporizador controlado por pin externo (Gated timer)

• Modo contador asincrónico (únicamente Timers tipo A)

• Modo contador sincrónico.

En los modos temporizador y Gated se utiliza como clock el ciclo de instrucción interna

Page 27: MPLAB C30 y PIC24

27

(Fcy), en cambio, en los modos contador sincronico y asincrónico se utiliza el clock externo

derivado del pin TxCK. Para control del modo de operación se utilizan los siguientes bits:

• TCS (TxCON<1>): Bit que determina fuente de clock.

• TSYC (TxCOM<2>): Bit para determinar si el clock externo se sincroniza con el ciclo de

instrucciones (Únicamente timers tipo A).

• TGATE (TxCON<6>): Bit que determina modo Gate.

El clock de entrada (FCY o pin TxCK) de todos los timers poseen la opción de ser

preescalado entre 1:1, 1:8, 1:64 o 1:256, esta configuración de preescaler se realiza mediante

los bits TCKPS<1:0> del registro TxCON. La cuenta de los preescaler es borrada cuando se

efectua una escritura sobre los registros TMRx y TxCON, cuando se deshabilita del timers

(TON=0) y al ocurrir un reset.

Los modos temporización, contador asincrónico y sincrónico son bien conocidos en las

familias 16F y 18F, por lo que solo voy a enfocarme en el modo Gate:

Cuando se selecciona el modo Gate, el timer puede ser usado para la medir la duración de

una señal externa. En este modo el timer se incrementa al recibir un flanco ascendente en el

pin TxCK y continua incrementándose a razón del clock interno (FCY) mientras este se

mantenga en nivel alto (1). Cuando se recibe un flanco descendente en el pin TxCK se

detiene la cuenta y se genera una interrupción.

Para seleccionar este modo hay que setear el bit TGATE y borrar el bit TCS. Además se

debe configurar el pin TxCK como entrada.

main(void){

T1CONbits.TCS = 0; // Selección de clock interno.

T1CONbits.TGATE = 1;// Habilitación modo Gate

T1CONbits.TCKPS = 0b00; // Preescaler 1:1

TMR1 = 0x00; // Inicialización

PR1 = 9; // Fijamos periodo

IPC0bits.T1IP = 0x01;// Nivel de prioridad 1 para interrupción

IFS0bits.T1IF = 0; // Borramos flag.

IEC0bits.T1IE = 1; // Habilitamos interrupción.

T1CONbits.TON = 1; // Encendemos timer.

while(1){}

}

Page 28: MPLAB C30 y PIC24

28

void __attribute__((__interrupt__, __shadow__)) _T1Interrupt(void){

static unsigned int Cuenta;

Cuenta=TMR1;

IFS0bits.T1IF = 0; // Borramos flag.

}

Configuracion timer 32-bits.

Mediante la combinación de los timers de tipo B y C logramos un timers de 32-bits, en

donde el timer tipo C es la palabra más significativa (msw) y el timer tipo B de menor

significancia (lsw).

Cuando los timers están configurados para trabajar a 32-bits únicamente los bits del registro

de control (TxCON) del timer tipo B son utilizados, a excepción del bit TSIDL los demás

bits del timer tipo C son ignorados. Pero para el control de la interrupción, se utilizan los bits

de control (TxIE, TxIF y TxIP) del timer tipo C.

Operación de lectura y escritura.

Para operaciones de lectura y escritura se utiliza un registro adicional llamado TMRyHLD

asignado al timer tipo C (Únicamente en operación a 32-bits). Cuando se realiza una lectura,

primero se debe leer el valor del timer tipo B (TMRx), para que el valor de TMRy (Timer

tipo C) sea transferido a TMRyHLD para la posterior lectura. En cambio al realizar una

escritura primero se debe establecer el valor de TMRyHLD para luego realizar la carga

sobre el valor TMRx, lo cual genera que el valor pre-cargado a TMRyHLD sea transferido a

TMRy.

Ejemplo:

Page 29: MPLAB C30 y PIC24

29

main(void){

T2CONbits.T32 = 1; // modo de 32-bits

T2CONbits.TCS = 0; // Clock interno

T2CONbits.TGATE = 1; // Mode Gate Habilitado, pin T2CK.-

T2CONbits.TCKPS = 0b00 // Preescaler 1:1

TMR3HLD = 0x00; // Borramos Timer3 (msw)

TMR2 = 0x00; // Borramos timer2 (lsw)

PR3 = 0x0002; // Fijamos periodo (msw)

PR2 = 0x0000; // Fijamos periodo (lsw)

IPC2bits.T3IP = 0x01; // Fijamos nivel de prioridad

IFS0bits.T3IF = 0; // Borramos flag.

IEC0bits.T3IE = 1; // Habilitamos interrupción.

T2CONbits.TON = 1; // Encendemos timer de 32-bits.

}

void __attribute__((__interrupt__, __shadow__)) _T3Interrupt(void){

static unsigned long Cuenta;

Cuenta=TMR2;

Cuenta+= TMR3HLD*0x10000;

IFS0bits.T3IF = 0; //Borramos flag.

}

Ejemplo modo temporización, interrupción cada 500ms a 40 MIPS.

/* ********* timers en C30 *************

\Autor: Suky

\Fecha: 07/01/10

*/

#include <p24hj128gp502.h>

_FOSCSEL(IESO_OFF & FNOSC_PRIPLL); // Oscilador primario + PLL

_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock y

Fail-Safe Clock des-habilitados, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

void __attribute__((__interrupt__, __shadow__)) _T3Interrupt(void){

LATBbits.LATB15=~LATBbits.LATB15; // Cambiamos de estado pin

B15.-

IFS0bits.T3IF = 0; //Borramos flag.

}

main(void){

// Cristal de 4MHz y PLL trabajando a 40 MIPS.

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

AD1PCFGL=0xFFFF; // todos los pines digitales.-

TRISB=0x0FFF; // Configuramos RB2-RB15 como salidas.-

LATB=0xF000; // Todos los leds apagados.-

T2CONbits.T32 = 1; // modo de 32-bits

T2CONbits.TCS = 0; // Clock interno

Page 30: MPLAB C30 y PIC24

30

T2CONbits.TGATE = 0; // Mode Gate deshabilitado.

T2CONbits.TCKPS = 0b00; // Preescaler 1:1

/* ** Debemos contar 20.000.000 ciclos y generar la interrupción

** */

PR3 = 0x0131; // Fijamos periodo (msw)

PR2 = 0x2D00; // Fijamos periodo (lsw)

IPC2bits.T3IP = 0x01; // Fijamos nivel de prioridad

IFS0bits.T3IF = 0; // Borramos flag.

IEC0bits.T3IE = 1; // Habilitamos interrupción.

TMR3HLD = 0x00; // Borramos Timer3 (msw)

TMR2 = 0x00; // Borramos timer2 (lsw)

T2CONbits.TON = 1; // Encendemos timer de 32-bits.

while(1){}

}

También podemos utilizar las funciones de las librerías que incluyen C30 para el manejo de

periféricos. Documentación sobre ella encontramos en …\Microchip\MPLAB

C30\docs\periph_lib . Para el control de de timers tenemos:

Funciones: • CloseTimerx

• CloseTimerxy

• ConfigIntTimerx

• ConfigIntTimerxy

• OpenTimerx

• OpenTimerxy

• ReadTimerx

• ReadTimerxy

• WriteTimerx

• WriteTimerxy

Los parámetros disponibles para cada función están bien detallados en la ayuda.

/* ********* timers en C30 *************

\Autor: Suky

\Fecha: 07/12/09

*/

#include <p24hj128gp502.h>

/* ***************************************************************** */

_FOSCSEL(IESO_OFF & FNOSC_PRIPLL); // Oscilador primario + PLL

_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock y

Fail-Safe Clock des-habilitados, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

/* ***************************************************************** */

#include <timer.h>

/* ***************************************************************** */

void __attribute__((__interrupt__, __shadow__)) _T3Interrupt(void){

LATBbits.LATB15=~LATBbits.LATB15; // Cambiamos de estado pin

B15.-

Page 31: MPLAB C30 y PIC24

31

_T3IF = 0; //Borramos flag.

}

/* ***************************************************************** */

main((int argc, char * argv[]){

// Cristal de 4MHz y PLL trabajando a 40 MIPS.

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

while(_LOCK==0);

AD1PCFGL=0xFFFF; // todos los pines digitales.-

TRISB=0x0FFF; // Configuramos RB2-RB15 como salidas.-

LATB=0xF000; // Todos los leds apagados.-

OpenTimer23(T2_ON & T2_IDLE_STOP & T2_GATE_OFF & T2_PS_1_1 &

T2_SOURCE_INT,0x01312D00);

ConfigIntTimer23(T3_INT_PRIOR_1 & T3_INT_ON);

WriteTimer23(0x00);

while(1){}

}

#include <p24fj128ga010.h>

_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & BKBUG_OFF & COE_OFF & ICS_PGx2

& FWDTEN_OFF & WINDIS_OFF & FWPSA_PR128 & WDTPS_PS32768);

_CONFIG2( FNOSC_PRI & FCKSM_CSDCMD & POSCMOD_HS );

#include <timer.h>

/* ***************************************************************** */

void __attribute__((__interrupt__, __shadow__)) _T3Interrupt(void){

LATDbits.LATD8=~LATDbits.LATD8; // Cambiamos de estado pin F2.-

_T3IF = 0; //Borramos flag.

}

int main (int argc, char * argv[]){

TRISD=0xF0FF;

OpenTimer23(T23_ON & T2_IDLE_STOP & T2_GATE_OFF & T2_PS_1_1 &

T2_SOURCE_INT,0x004C4B40);

ConfigIntTimer23(T3_INT_PRIOR_1 & T3_INT_ON);

WriteTimer23(0x00);

while(1){

}

}

Que desperdicio hacer titilar un led con tremenda maquina pero es el precio de ir

adquiriendo conocimientos de forma ordenada y no estamparnos contra la pared y no saber

para donde correr.

Page 32: MPLAB C30 y PIC24

32

Selección de pines de los periféricos.

Esta característica que implementa esta familia de microcontroladores esta enfocado a hacer

mucho más flexible la utilización de ellos, más aun en microcontroladores de pocos pines.

La selección de pines de los periféricos permite a los usuarios determinar que pines usar en

una amplia gama de pines I/O. Al aumentar las opciones de patillas disponibles en un

dispositivo en particular, podemos adaptar mejor el microcontrolador a la aplicación, en

lugar de adecuar la aplicación al dispositivo.

Los pines disponibles para selección dependen del microcontrolador particularmente. Estos

pines incluyen la designación RPn (Remappable peripheral y n indica el número)

Los periféricos que pueden utilizar la selección de pines son únicamente digitales, estos

incluyen las comunicaciones seriales (UART y SPI), entrada a clock externo de timers, pin

para captura o comparación y pines de interrupción externa. Hay periféricos digitales que no

pueden utilizar la multiplexión de pines por necesitar un hardware especial, como es el caso

de la comunicación I2C.

Control de la selección de pines.

La selección de pines se divide en pines que tienen la función de entrada y pines de salida.

Para el mapeo de pines con la función de entrada se utiliza el registro RPINRx, que contiene

un campo de 5-bits para seleccionar el pin RPx asociado al periférico.

Page 33: MPLAB C30 y PIC24

33

En cambio la selección de pines con la función de entrada es inversa, con la utilización del

registro RPORx se asigna un periférico a un pin determinado. Además tiene la opción

NULL (00000) que permite dejar desconectado el pin. (Sin selección)

Durante la operación normal del dispositivo, la operación de escritura de los registros

RPINRx y RPORX son deshabilitadas. Para desbloquear esta protección es necesario actuar

sobre el bit IOLOCK (OSCCON<6>), para ello es necesario realizar una secuencia de

comandos.

• Escribir 0x46 en OSCCON<7:0>

• Escribir 0x57 en OSCCON<7:0>

• Borrar o setear bit IOLOCK en una sola operación.

Ejemplo de asignación de pines:

Page 34: MPLAB C30 y PIC24

34

// Habilitamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON & ~(0x40));

RPINR18bits.U1RXR = 9; //Asignamos pin recepción UART1 a RP9.-

RPOR4bits. RP8R = 0b00011; // Asignamos RP8 al pin de transmisión UART1.

// Bloqueamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON |0x40);

UART.

El módulo USART es utilizado para comunicación serial con dispositivos periféricos o

computadoras, utilizando protocolos tales copmo RS232, RS485, LIN o IrDA. Soporta

control de flujo por hardware con los pines UxCTS y UxRTS, además incluye

decodificación y codificación IrDA.

Las características de este módulo son:

• Full-Duplex, 8 o 9 bits de datos.

• Para datos de 8 bits se puede asignar paridad par, impar o ninguna.

• 1 o 2 bits STOP.

• Auto-Baud Rate.

• Opción de control de flujo mediante hardware, pines UxCTS y UxRTS.

• Generador de baud Rate con preescaler de 16-bits.

• Rango de Baud Rate de 10 Mbps a 38 bps a 40 MIPS.

• Buffer de 4 posiciones FIFO.

• Detección de error de paridad, error de cuadro y error de sobre-escritura.

• Soporte para modo de 9-bits con detección de dirección.

• Modo Loopback, devuelve inmediatamente el carácter recibido.

• Interrupción por transmisión y recepción.

• Codificador y decodificador IrDA.

• Soporte para bus LIN.

Registros de control.

Los registros utilizados para el control del módulo UART son:

• UxMODE:

o Habilita o deshabilita modulo UART.

o Habilita o deshabilita codificación y decodificación IrDA.

o Habilita o deshabilita pines UzRTS y UxCTS.

o Configura el modo de operación designado al pin UxRTS.

o Configura la polaridad del pin UxRx.

o Selecciona tipo de baud rate.

o Selecciona número de datos, paridad y bit de Stop.

• UxSTA:

o Selecciona el modo de interrupción de transmisión.

o Selecciona modo de interrupción de recepción.

o Habilita o deshabilita transmisión.

o Controles para modo de detección de direccionamiento.

o Indicadores varios de estado de buffer de transmisión y recepción, y errores.

• UxRXREG:

Page 35: MPLAB C30 y PIC24

35

o Guarda el dato recibido.

• UxTXREG:

o Para cargar datos a transmitir.

• UxBRG:

o Para determinar Baud Rate.

Baud rate.

Para determinar el baud rate tenemos 2 opciones, baud rate de alta velocidad BRGH=1 o de

baja velocidad. Las ecuaciones asociadas son: (UxBRG de 16-bits)

Configuración de la USART.

La USART usa el formato estándar NRZ, con un bit de Start, 8 o 9 bits de datos y 1 o 2 bits

de Stop. Además tenemos la posibilidad de configurar paridad como par, impar o ninguna.

Esto lo configuramos mediante los bits PDSEL<1:0> y STSEL. (UxMODE)

El módulo USART es habilitado mediante el seteo del bit UARTEN (UxMODE) y el bit

UTXEN (UxSTA). Una vez habilitado, los pines UxTX y UxRX son configurados como

Page 36: MPLAB C30 y PIC24

36

salida y entrada respectivamente, sustituyendo los valores seteados al TRIS correspondiente.

Para deshabilitar el módulo se debe borrar el bit UARTEN (UxMODE), al deshabilitarlo los

pines se comportan con I/O adoptando la configuración del TRIS. Además se borra el buffer,

el baud rate se restablece y también todos los bits de estados y errores son reseteados.

Transmisión.

El modo de funcionamiento es más o menos similar a las otras familias, difiere en que hay 3

modos de selección de la interrupción:

• UTXISEL<1:0>=00, el bit UxTXIF es seteado cuando un carácter es transferido desde el

Buffer al registro UxTSR, esto implica que una posición esta libre en el buffer.

• UTXISEL<1:0>=01, el bit UxTXIF es seteado cuando el ultimo carácter es transferido

desde el registro UxTSR, esto implica que todas las operaciones de transmisión han sido

completadas.

• UTXISEL<1:0>=10, el bit UxTXIF es seteado cuando el carácter es transferido al

registro UxTSR y el buffer de transmisión está vacio.

Pasos a seguir para la transmisión. 1. Inicializar Baud Rate cargando el valor apropiado a UxBRG.

2. Setear el numero de bits de datos, numero de bits de Stop y seleccionar paridad.

3. Si se trabajará con interrupciones seleccionar entre los 3 modos de operación, habilitar

mediante bit UxTIE y seleccionar nivel de prioridad (UxTXIP<2:0>).

4. Habilitar modulo UART setenado bit UARTEN.

5. Habilitar transmisión setenado el bit UTXEN.

6. El bit UxTXIF debe ser borrado por software en la rutina de servicio de interrupción.

7. Cargar el dato en el registro UxTXREG. Si se ha seleccionado transmisión de 9-bits se

debe cargar un Word y sino un byte. Se pueden cargar datos hasta que el bit UxSTA sea

seteado a uno indicando buffer lleno.

Recepción.

Al igual que el bloque de transmisión, no hay nada de nuevo en el bloque de recepción, el

principio de funcionamiento se mantiene. Las interrupciones pueden configurarse de los

siguientes modos:

• URXISEL<1:0>=00 o 01. La interrupción es generada cada vez que un dato es

transferido desde el registro UxRSR al buffer de recepción. Puede haber uno o más

caracteres en el buffer.

• URXISEL<1:0>=10. La interrupción es generada cuando un dato es transferido desde el

registro UxRSR al buffer, y como resultado, el buffer tiene 3 de 4 caracteres.

• URXISEL<1:0>=11. La interrupción es generada cuando un dato es transferido desde el

registro UxRSR al buffer, y como resultado, el buffer tiene 4 caracteres.

Estos modos pueden ser cambiando durante la operación. Tenemos los bits URXDA y

UxRXIF que indican el estado del registro UxRXREG, y el bit RIDLE (UxSTA<4>) que

muestra el estado del registro UxRSR. Cuando el registro UxRSR esta vacio, el bit RIDLE

está en 1.

Pasos a seguir en la recepción. 1. Inicializar Baud Rate.

2. Configurar el número de bits de datos, Stop y determinar paridad.

Page 37: MPLAB C30 y PIC24

37

3. Si se utilizará interrupción determinar el modo mediante los bits URXISEL, habilitar la

interrupción mendiante el bit UxRXIE, y establecer prioridad (UxRXIP).

4. Habilitar modulo UART seteando bit UARTEN.

5. Si la interrupción no está habilitada, se debe recibir por poleo mediante el bit URXDA.

Si se usa interrupción, el bit UxRXIF debe ser borrado por software.

6. Leer dato desde buffer, si se ha seleccionado trabajar con 9-bits se recibe un Word, sino

un byte.

Hay varias otras operaciones que se pueden realizar que dependerán de la aplicación a

desarrollar, como transmisión a 9-bits, detectar automáticamente dirección, modo Loopback,

auto-Baud Rate, operación con control de flujo mediante hardware, soporte infrarrojo,

soporte LIN, ect. cuya información esta bien detallada en el datasheet del dispositivo.

Ejemplo:

Configuramos UART1 en 9600:8:N:1 para enviar un texto de inicialización y luego por

medio de interrupción capturar dato enviado por PC y luego reenviar el mismo dato.

Tambien debemos asignar los pines del periferico, en este caso Rx a RP9 y Tx a RP8.

Configurando bits a bits:

/* ********* UART en C30 *************

\Autor: Suky

\Fecha: 09/01/10

*/

#include <p24hj128gp502.h>

/* **

**************************************************************************

********* ** */

_FOSCSEL(IESO_OFF & FNOSC_PRI); // Oscilador primario

_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock

habilitad, Fail-Safe Clock des-habilitado, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

/* **

**************************************************************************

********* ** */

#include <uart.h>

char DataUART;

char KbhitUART;

/* **

**************************************************************************

********* ** */

// Definciones para calculo de Baud Rate.-

#define FCY 40000000 // MHz

#define BAUDRATE 9600

#define BRGHigh 1

#if BRGHigh

#define BRGVAL ((FCY/BAUDRATE)/4)-1

#else

#define BRGVAL ((FCY/BAUDRATE)/16)-1

Page 38: MPLAB C30 y PIC24

38

#endif

/* **

**************************************************************************

********* ** */

void __attribute__((__interrupt__, __shadow__)) _U1RXInterrupt(void){

DataUART=getcUART1();

KbhitUART=1;

_U1RXIF=0; // Borramos flag.

}

/* **

**************************************************************************

********* ** */

main(void){

const char Texto[]= "Probando comunicación serial...\r\nPIC24HJ128GP502 xD

...\r\n";

// Configuramos PLL para trabajar a 40 MIPS con Cristal de 4MHz.-

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

// Cambiamos clock para incorporar PLL.-

__builtin_write_OSCCONH(0b011); // Indicamos cambio a clock

primario con PLL.-

__builtin_write_OSCCONL(0x01); // Iniciamos cambio.-

while(OSCCONbits.OSWEN == 1) {}; // Esperamos a que se termine el

cambio de clock.

// Habilitamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON & ~(0x40));

RPINR18bits.U1RXR = 9; //Asignamos pin recepción UART1 a RP9.-

RPOR4bits. RP8R = 0b00011; // Asignamos RP8 al pin de transmisión

UART1.

// Bloqueamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON |0x40);

U1MODEbits.USIDL=1; // Al pasar al modo Sleep, apagar modulo.

U1MODEbits.IREN=0; // Codificacion/decodificacion IrDA

deshabilitado.

U1MODEbits.RTSMD=1; // RTS en modo simple.

U1MODEbits.UEN=0b00; // Tx/Rx habilitado, CTS/RTS deshabilitados.

U1MODEbits.WAKE=0; // Wake-up deshabilitado.

U1MODEbits.LPBACK=0; // Modo LoopBack deshabilitado.

U1MODEbits.ABAUD=0; // Auto-Baud rate deshabilitado.

U1MODEbits.URXINV=0; // Modo normal de pin RX, en reposo = 1.

U1MODEbits.BRGH=BRGHigh; // Mode seleccionado en define.-

U1MODEbits.PDSEL=0b00; // 8 bits- sin paridad.

U1MODEbits.STSEL=0; // 1 bit de Stop.

U1STAbits.UTXISEL1=1; // Interrupcion por transimision no usada.

U1STAbits.UTXISEL0=1; // Interrupcion por transimision no usada.

U1STAbits.UTXINV=0; // Pin Tx en reposo igual a 1.

U1STAbits.UTXBRK=0; // Break deshabilitado.

U1STAbits.URXISEL=0b00; // Interrumpir cuando se recibe un

caracter.

U1STAbits.ADDEN=0; // Auto-Address deshabilitado.

U1STAbits.OERR=0; // Borramos bits de error por overflow.

U1BRG =BRGVAL; // Baud Rate determinado en defines.

IPC2bits.U1RXIP=0b01; // Fijamos nivel de prioridad.

Page 39: MPLAB C30 y PIC24

39

IFS0bits.U1RXIF=0; // Borramos flag.

IEC0bits.U1RXIE=1; // habilitamos interrupcion por recepción.

U1MODEbits.UARTEN=1;// Encendemos modulo.

U1STAbits.UTXEN=1; // Transmision habilitada.

KbhitUART=0;

putsUART1(Texto);

while(1){

if(KbhitUART==1){

KbhitUART=0;

putcUART1(DataUART);

}

}

}

Para el control de UART C30 dispone de la librería uart.h la cual tiene las siguientes

funciones:

• BusyUARTx

• CloseUARTx

• ConfigIntUARTx

• DataRdyUARTx

• getsUARTx

• OpenUARTx [dsPIC30F]

• OpenUARTx [dsPIC33F/PIC24H]

• putsUARTx

• ReadUARTx o getcUARTx

• WriteUARTx o putcUARTx

/* ********* UART en C30 *************

\Autor: Suky

\Fecha: 09/01/10

*/

#include <p24hj128gp502.h>

/* **

**************************************************************************

********* ** */

_FOSCSEL(IESO_OFF & FNOSC_PRI); // Oscilador primario

_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock

habilitad, Fail-Safe Clock des-habilitado, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

/* **

**************************************************************************

********* ** */

#include <uart.h>

char DataUART;

char KbhitUART;

/* **

**************************************************************************

********* ** */

Page 40: MPLAB C30 y PIC24

40

// Definciones para calculo de Baud Rate.-

#define FCY 40000000 // MHz

#define BAUDRATE 9600

#define BRGHigh 1

#if BRGHigh

#define BRGVAL ((FCY/BAUDRATE)/4)-1

#else

#define BRGVAL ((FCY/BAUDRATE)/16)-1

#endif

/* **

**************************************************************************

********* ** */

void __attribute__((__interrupt__, __shadow__)) _U1RXInterrupt(void){

DataUART=getcUART1();

KbhitUART=1;

_U1RXIF=0; // Borramos flag.

}

/* **

**************************************************************************

********* ** */

main(void){

const char Texto[]= "Probando comunicación serial...\r\nPIC24HJ128GP502 xD

...\r\n";

// Configuramos PLL para trabajar a 40 MIPS con Cristal de 4MHz.-

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

// Cambiamos clock para incorporar PLL.-

__builtin_write_OSCCONH(0b011); // Indicamos cambio a clock

primario con PLL.-

__builtin_write_OSCCONL(0x01); // Iniciamos cambio.-

while(OSCCONbits.OSWEN == 1) {}; // Esperamos a que se termine el

cambio de clock.

// Habilitamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON & ~(0x40));

RPINR18bits.U1RXR = 9; //Asignamos pin recepción UART1 a RP9.-

RPOR4bits. RP8R = 0b00011; // Asignamos RP8 al pin de transmisión

UART1.

// Bloqueamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON |0x40);

/* ** Utilizando las funciones definidas en C30 ** */

OpenUART1(UART_EN & UART_IDLE_STOP & UART_IrDA_DISABLE &

UART_MODE_SIMPLEX &

UART_UEN_00 & UART_DIS_WAKE & UART_DIS_LOOPBACK &

UART_UXRX_IDLE_ONE &

UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_BRGH_FOUR

& UART_1STOPBIT,

UART_INT_TX & UART_IrDA_POL_INV_ZERO &

UART_SYNC_BREAK_DISABLED &

UART_TX_ENABLE & UART_INT_RX_CHAR &

UART_ADR_DETECT_DIS & UART_RX_OVERRUN_CLEAR,

BRGVAL);

SetPriorityIntU1RX(1);

EnableIntU1RX;

Page 41: MPLAB C30 y PIC24

41

KbhitUART=0;

putsUART1(Texto);

while(1){

if(KbhitUART==1){

KbhitUART=0;

putcUART1(DataUART);

}

}

}

#include <p24fj128ga010.h>

_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & BKBUG_OFF & COE_OFF & ICS_PGx2

& FWDTEN_OFF & WINDIS_OFF & FWPSA_PR128 & WDTPS_PS32768);

_CONFIG2( FNOSC_PRI & FCKSM_CSDCMD & POSCMOD_HS );

#include <uart.h>

char DataUART;

char KbhitUART;

/* ** ************************************************************** ** */

// Definciones para calculo de Baud Rate.-

#define FCY 10000000 // MHz

#define BAUDRATE 9600

#define BRGHigh 1

#if BRGHigh

#define BRGVAL ((FCY/BAUDRATE)/4)-1

#else

#define BRGVAL ((FCY/BAUDRATE)/16)-1

#endif

//__attribute__ ((__section__ (".text"), __space__(prog)))

const char Texto[]= "Probando comunicación serial...\r\nPIC24FJ128GA010

xD ...\r\n";

/* ** ************************************************************* ** */

void __attribute__((__interrupt__, __shadow__)) _U2RXInterrupt(void){

DataUART=getcUART2();

KbhitUART=1;

_U2RXIF=0; // Borramos flag.

}

/* ** ************************************************************** ** */

int main (int argc, char * argv[]){

/* ** Utilizando las funciones definidas en C30 ** */

OpenUART2(UART_EN & UART_IDLE_STOP & UART_IrDA_DISABLE &

UART_MODE_SIMPLEX &

UART_UEN_00 & UART_DIS_WAKE & UART_DIS_LOOPBACK &

UART_UXRX_IDLE_ONE &

UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_BRGH_FOUR &

UART_1STOPBIT,

0xFFFF & UART_IrDA_POL_INV_ZERO &

UART_SYNC_BREAK_DISABLED &

UART_TX_ENABLE & UART_INT_RX_CHAR & UART_ADR_DETECT_DIS

& UART_RX_OVERRUN_CLEAR,

Page 42: MPLAB C30 y PIC24

42

BRGVAL);

ConfigIntUART2(UART_RX_INT_EN & UART_RX_INT_PR1 & UART_TX_INT_DIS &

UART_TX_INT_PR0 );

/*U2MODEbits.USIDL=1; // Al pasar al modo Sleep, apagar modulo.

U2MODEbits.IREN=0; // Codificacion/decodificacion IrDA

deshabilitado.

U2MODEbits.RTSMD=1; // RTS en modo simple.

U2MODEbits.UEN=0b00; // Tx/Rx habilitado, CTS/RTS deshabilitados.

U2MODEbits.WAKE=0; // Wake-up deshabilitado.

U2MODEbits.LPBACK=0; // Modo LoopBack deshabilitado.

U2MODEbits.ABAUD=0; // Auto-Baud rate deshabilitado.

U2MODEbits.RXINV=0; // Modo normal de pin RX, en reposo = 1.

U2MODEbits.BRGH=BRGHigh; // Mode seleccionado en define.-

U2MODEbits.PDSEL=0b00; // 8 bits- sin paridad.

U2MODEbits.STSEL=0; // 1 bit de Stop.

U2STAbits.UTXISEL1=1; // Interrupcion por transimision no usada.

U2STAbits.UTXISEL0=1; // Interrupcion por transimision no usada.

U2STAbits.UTXINV=0; // Pin Tx en reposo igual a 1.

U2STAbits.UTXBRK=0; // Break deshabilitado.

U2STAbits.URXISEL=0b00; // Interrumpir cuando se recibe un caracter.

U2STAbits.ADDEN=0; // Auto-Address deshabilitado.

U2STAbits.OERR=0; // Borramos bits de error por overflow.

U2BRG =BRGVAL; // Baud Rate determinado en defines.

IPC7bits.U2RXIP=0b01; // Fijamos nivel de prioridad.

IFS1bits.U2RXIF=0; // Borramos flag.

IEC1bits.U2RXIE=1; // habilitamos interrupcion por recepción.

U2MODEbits.UARTEN=1;// Encendemos modulo.

U2STAbits.UTXEN=1; // Transmision habilitada.*/

KbhitUART=0;

putsUART2(Texto);

while(1){

if(KbhitUART==1){

KbhitUART=0;

putcUART2(DataUART);

}

}

}

Uso del printf en C30.

Para utilizar printf en nuestros proyectos se debe agregar la librería stdio.h, pero además se

Page 43: MPLAB C30 y PIC24

43

debe inicializar el Heap Size, yendo a Project/Buil Options/Project, y en la solapa

MPLAB LINK30 llenar el campo, yo he colocado 256.

Notificación de cambio de estado de pines.

Los PIC24 disponen de la habilidad de generar una interrupción en respuesta de cambio de

estado en los pines seleccionados para ello. El número de pines disponibles para tal

actividad depende del microcontrolador elegido.

Se dispone de 4 registros para el control de la operación, los registros CNEN1, CNEN2,

CNPU1 y CNPU2. Los primeros dos registros contienen los bits CNxIE (Donde x indica el

numero de pin) que son utilizados para asociar la interrupción con el pin. En los registros

CNPU1 y CNPU2 encontramos los bits CNxPUE utilizados para habilitar las resistencias

pull-up internas a cada pin, de manera de ahorrarnos esta al utilizar por ejemplo teclados

matriciales.

Configuración:

1. Colocar los pines como entrada digital setenado los bits correspondientes en el TRISx.

2. Habilitar la interrupción para los pines seleccionados seteando los bits correspondientes

en CNEN1 y CNEN2.

3. Si se desea habilitar las resistencias pull-up internas setear los bits correspondientes en

CNPU1 y CNPU2.

4. Fijar prioridad de interrupción usando los bits CNIP<2:0>.

5. Habilitar interrupción seteando bit CNIE.

Cuando la interrupción ocurre, se debe leer el registro PORT asociado al pin necesario para

detectar la condición de desigualdad y preparar la lógica para detectar un próximo cambio.

Interrupción externa

También disponemos interrupción por defección de flanco ascendente o descendente de un

pin. El número de pines disponibles es como máximo 5 pero depende del dispositivo. Para

selección del flanco de activación de evento tenemos los bits INTxEP del registro

INTCON2, además de los bits INTxIP<2:0> para configurar prioridad de interrupción y

INTxIE para habilitar dicha interrupción. Los pines para trabajar con esta propiedad deben

elegirse mediante la selección de pines de periféricos (PPS) excepto INT0 que en este caso

es fijo en RB7.

Funciones disponible en C30 para trabajar con notificación de cambio de estado e

interrupción externa:

• CloseINTx

• ConfigINTx

• ConfigCNPullups

• ConfigIntCN

Nota: Revisando las funciones vemos que para control de cambio solo se tiene en cuenta

desde 0 hasta 23, pero este PIC tiene 24, 27, 29 y 30 Así que nos utilizables, hay que

Page 44: MPLAB C30 y PIC24

44

utilizar configuración mediante bits.

Ejemplo, leyendo un teclado matricial:

/* ********* NC en C30 *************

\Autor: Suky

\Fecha: 12/01/10

*/

#include <p24hj128gp502.h>

/* ** ************************************************************** ** */

_FOSCSEL(IESO_OFF & FNOSC_PRI); // Oscilador primario

_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock

habilitad, Fail-Safe Clock des-habilitado, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

/* ** ************************************************************* ** */

#define FCY 40000000UL // Necesaria para delays y UART.

#include <libpic30.h>

#include <uart.h>

#include <ports.h>

/* ** ************************************************************** ** */

// Definciones para calculo de Baud Rate.-

#define BAUDRATE 9600

#define BRGHigh 1

#if BRGHigh

#define BRGVAL ((FCY/BAUDRATE)/4)-1

#else

#define BRGVAL ((FCY/BAUDRATE)/16)-1

#endif

#define Col0 PORTBbits.RB12

#define Col1 PORTBbits.RB13

#define Col2 PORTBbits.RB14

#define Col3 PORTBbits.RB15

#define Fil0 LATBbits.LATB1

#define Fil1 LATBbits.LATB0

#define Fil2 LATAbits.LATA1

#define Fil3 LATAbits.LATA0

/* ** ************************************************************** ** */

const char Teclas[17]={'1','2','3','A',

'4','5','6','B',

'7','8','9','C',

'*','0','#','D'};

char Tecla,KbhitTecla;

/* ** ************************************************************** ** */

void __attribute__((__interrupt__, __shadow__)) _CNInterrupt(void){

volatile char i,j=0;

__delay_ms(30);

Fil0=1;Fil1=1;Fil2=1;Fil3=1;

for(i=0;i<4;i++){

switch(i){

case 0: Fil0=0;break;

case 1: Fil1=0;Fil0=1;break;

case 2: Fil2=0;Fil1=1;break;

case 3: Fil3=0;Fil2=1;break;

}

if(!Col0) break;

Page 45: MPLAB C30 y PIC24

45

j++;

if(!Col1) break;

j++;

if(!Col2) break;

j++;

if(!Col3) break;

j++;

}

Fil0=0;Fil1=0;Fil2=0;Fil3=0;

while((!Col0)|(!Col1)|(!Col2)|(!Col3));

__delay_ms(30);

if(j<16){

Tecla=Teclas[j];

KbhitTecla=1;

}

LATB=PORTB;

_CNIF=0; // Borramos flag.

}

/* ** ************************************************************** ** */

main(void){

// Configuramos PLL para trabajar a 40 MIPS con Cristal de 4MHz.-

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

// Cambiamos clock para incorporar PLL.-

__builtin_write_OSCCONH(0b011); // Indicamos cambio a clock

primario con PLL.-

__builtin_write_OSCCONL(0x01); // Iniciamos cambio.-

while(OSCCONbits.OSWEN == 1) {}; // Esperamos a que se termine el

cambio de clock.

// Habilitamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON & ~(0x40));

_U1RXR = 9; //Asignamos pin recepción UART1 a RP9.-

_RP8R = 0b00011; // Asignamos RP8 al pin de transmisión UART1.

// Bloqueamos escritura sobre registros RPINRx y RPORX.

__builtin_write_OSCCONL(OSCCON |0x40);

OpenUART1(UART_EN & UART_IDLE_STOP & UART_IrDA_DISABLE &

UART_MODE_SIMPLEX &

UART_UEN_00 & UART_DIS_WAKE & UART_DIS_LOOPBACK &

UART_UXRX_IDLE_ONE &

UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_BRGH_FOUR &

UART_1STOPBIT,

UART_INT_TX & UART_IrDA_POL_INV_ZERO &

UART_SYNC_BREAK_DISABLED &

UART_TX_ENABLE & UART_INT_RX_CHAR &

UART_ADR_DETECT_DIS & UART_RX_OVERRUN_CLEAR,

BRGVAL);

/* ** Configuramos para interrupcion por cambio de estado en pines

RB12,13,14 y 15 ** */

_CN11IE=1;_CN12IE=1;_CN13IE=1;_CN14IE=1;

_CN11PUE=1;_CN12PUE=1;_CN13PUE=1;_CN14PUE=1;

printf("Probando deteccion de tecla pulsada:\r\n\r\n");

AD1PCFGL=0xFFFF; // todos los pines digitales.-

TRISA=0xFFFC;

TRISB=0xFFFC;

Fil0=0;Fil1=0;Fil2=0;Fil3=0;

Page 46: MPLAB C30 y PIC24

46

_CNIP=1; // Prioridad en 1.

_CNIE=1; // Habilitamos interrupción.

KbhitTecla=0;

while(1){

if(KbhitTecla){

KbhitTecla=0;

printf("Tecla:-> %c\r\n",Tecla);

}

}

}

Control LCD. Modo 4-bits o 3-pines más registro de desplazamiento

En este caso solo posteo una librería para manejo LCD compatible con Hitachi 44780 y que

modificaciones hacer para usar printf para escribir en el LCD. La librería dispone de la

posibilidad de trabajar en modo de 4-bits con R/W siempre y cuando se seleccionen pines

tolerables a 5V para dispositivos de 3.3V o sin R/W. También implementa el manejo del

LCD mediante 3 pines y un registro de desplazamiento (4094, 74164, ect) todos estos modos

se selecciona con 2 definiciones (USE_RW y USE_3PINES).

Tiene implementada las siguientes funciones:

• Open_LCD(Tipo)

o Tipo = LINEA_5X7,LINEA_5X10 o LINEAS_5X7

• BusyLCD();

• WriteCmdLCD(cmd)

• putcLCD(data) .Con reconocimiento de caracteres especiales ‘\f’,’\n’

• putsLCD(*data)

• GotoxyLCD(x,y)

Ejemplo:

Modo 4 bits con R/W:

Modo 3 pines y registro de desplazamiento:

Page 47: MPLAB C30 y PIC24

47

Modificación de funciones de bajo nivel de la librería stdio.h para re-direccionar salida de datos de printf.

Podemos utilizar la función printf para formatear datos y enviar por puerto UART, SPI,

CAN, I2C (Caso especial) y LCD entre otros ejemplos. Una forma fácil que encontré fue la

siguiente:

Agregar una variable externa a la función write.c (…\Microchip\MPLAB C30\src\pic30)

__OUT_PER que seleccionará que periférico utilizar para la salida de datos. Esta variable la

definimos en nuestro archivo principal y allí controlaremos su valor.

En el archivo write.c solo hacemos un switch para seleccionar los periféricos de salida de

datos manteniendo la original sin hacer cambios mayores:

extern int __OUT_PER;

switch(__OUT_PER){

case 0: // UART

while ((ustatus->TRMT) ==0);

*txreg = *(char*)buffer++;

break;

case 1: // LCD

WriteDataLCD(*(char*)buffer++);

break;

case 2: // SPI

break;

case 3: // CAN

break;

}

También debemos agregar la definición de la función externa utilizada, por ejemplo para el

caso del LCD:

extern void WriteDataLCD(char data);

Y no debemos olvidarnos de agregar el archivo write.c al proyecto para que sea re-

compilado y efectivizar los cambios. (Se adjunta write.c)

Quedando la librería con la modificación para usar printf en el envío de datos al LCD:

Page 48: MPLAB C30 y PIC24

48

Ejemplo para un PIC24F: Ver que se cambia las funciones que realizan la configuración

de fuses. Se configura para oscilador HS sin PLL, por ejemplo 20 MHZ, 10MIPS.

#include <p24fj128ga010.h>

/* ** ***************************************************************** */

_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & BKBUG_OFF & COE_OFF & ICS_PGx2

& FWDTEN_OFF & WINDIS_OFF & FWPSA_PR128 & WDTPS_PS32768);

_CONFIG2( FNOSC_PRI & FCKSM_CSDCMD & POSCMOD_HS );

/* ** ************************************************************** ** */

//#define FCY 10000000UL

#include "LCD_C30.h"

#include <stdio.h>

#define LCD_rev 1.0

int __OUT_PER=1;

/* ** ************************************************************** ** */

main(void){

OpenLCD(LINEAS_5X7);

printf("Probando LCD\n%C30 v:%1.1f",LCD_rev);

while(1){}

}

Ejemplo para un PIC24H:

/* ********* LCD en C30 *************

\Autor: Suky

\Fecha: 14/01/10

*/

#include <p24hj128gp502.h>

/* ** ************************************************************** ** */

_FOSCSEL(IESO_OFF & FNOSC_PRI); // Oscilador primario

_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); // Conmutación de Clock

habilitad, Fail-Safe Clock des-habilitado, OSC2 Clout, Cristal XT.

_FWDT(FWDTEN_OFF);// Watchdog des-habilitado.

_FPOR(FPWRT_PWR2);// Power-on Reset 2ms.

/* ** ************************************************************ ** */

#include "LCD_C30.h"

#include <stdio.h>

#define LCD_rev 1.0

int __OUT_PER=1;

/* ** ************************************************************** ** */

main(void){

// Configuramos PLL para trabajar a 40 MIPS con Cristal de 4MHz.-

PLLFBD = 0x004E; // M=80.

CLKDIV = 0x0000; //N1=2, N2=2.

// Cambiamos clock para incorporar PLL.-

__builtin_write_OSCCONH(0b011); // Indicamos cambio a clock

Page 49: MPLAB C30 y PIC24

49

primario con PLL.-

__builtin_write_OSCCONL(0x01); // Iniciamos cambio.-

while(OSCCONbits.OSWEN == 1) {}; // Esperamos a que se termine el

cambio de clock.

AD1PCFGL=0xFFFF; // todos los pines digitales.-

OpenLCD(LINEAS_5X7);

printf("Probando LCD\n%Ucontrol v:%1.1f",LCD_rev);

while(1){

}

}

Librerías en anexo A.

ANEXO A.

Librerías para control de LCD:

/*******************************************************************

\file LCD_30.h

\version: 1.0

\author Suky.

\web www.infopic.comlu.com

\mail [email protected]

\date 18/01/10

*******************************************************************/

#if defined(__dsPIC30F__)

#include <p30fxxxx.h>

#elif defined(__dsPIC33F__)

#include <p33Fxxxx.h>

#elif defined(__PIC24H__)

#include <p24Hxxxx.h>

#elif defined(__PIC24F__)

#include <p24Fxxxx.h>

#endif

#ifndef _LCDPIC24_H

#define _LCDPIC24_H

#warning "Se ha definido valor de FCY para realización de demoras!!!"

#define FCY 10000000UL

//#define FCY 2000000UL

#include <libpic30.h>

/* ** El siguiente modo no utiliza el PIN RW** */

#define USE_3PINES // Se utiliza registro de desplazamiento

para DATA[0..3] y RS.

Page 50: MPLAB C30 y PIC24

50

/* ** Para no utilizar el PIN RW comentar la siguiente definición ** */

//#define USE_RW

#ifndef USE_3PINES

/* ** Pines tolerables a 5V para usar RW ** */

#define DATA_PIN_7 LATBbits.LATB11

#define DATA_PIN_6 LATBbits.LATB10

#define DATA_PIN_5 LATBbits.LATB9

#define DATA_PIN_4 LATBbits.LATB8

#define READ_PIN_7 PORTBbits.RB11

#define READ_PIN_6 PORTBbits.RB10

#define READ_PIN_5 PORTBbits.RB9

#define READ_PIN_4 PORTBbits.RB8

#define TRIS_DATA_PIN_7 TRISBbits.TRISB11

#define TRIS_DATA_PIN_6 TRISBbits.TRISB10

#define TRIS_DATA_PIN_5 TRISBbits.TRISB9

#define TRIS_DATA_PIN_4 TRISBbits.TRISB8

#define E_PIN LATBbits.LATB2

#define RS_PIN LATBbits.LATB0

#ifdef USE_RW

#define RW_PIN LATBbits.LATB1

#endif

#define TRIS_E TRISBbits.TRISB2

#define TRIS_RS TRISBbits.TRISB0

#ifdef USE_RW

#define TRIS_RW TRISBbits.TRISB1

#endif

#else

#define DATA_PIN LATBbits.LATB15

#define CLOCK_PIN LATBbits.LATB14

#define E_PIN LATBbits.LATB13

#define TRIS_DATA TRISBbits.TRISB15

#define TRIS_CLOCK TRISBbits.TRISB14

#define TRIS_E TRISBbits.TRISB13

#endif

/* Definiciones generales */

// Encendido display.

#define D_ON 0x0C

#define D_OFF 0x08

#define CURSOR_ON 0x0E

#define BLINK_ON 0x0F

#define C_ON_BLINK 0x0D

// Control.

#define D_CLEAR 0x01

#define SET_INIT 0x02

/*;--- Modos de Entrada

; Incrementa Direccion, Display fijo 0x06

; Decrementa Direccion, Display fijo 0x04

; Incrementa Direccion, Cursor fijo 0x07

; Decrementa Direccion, Cursor fijo 0x05

;--- Corriemiento Cursor

; Cursor a la Izquierda 0x10

Page 51: MPLAB C30 y PIC24

51

; Cursor a la Derecha 0x14

; Display a la Izquierda 0x18

; Display a la Derecha 0x1C

;--- Fijar Sistema

; Bus 8 bits, 1 línea, 5x7 0x30

; Bus 8 bits, 1 linea, 5x10 0x34

; Bus 8 bits, 2 líneas, 5x7 0x38

; Bus 4 bits, 1 línea, 5x7 0x20

; Bus 4 bits, 1 linea, 5x10 0x24

; Bus 4 bits, 2 líneas, 5x7 0x28*/

/* Definiciones para OpenLCD */

#define LINEA_5X7 0b00000000 /* 5x7 characters, single line */

#define LINEA_5X10 0b00000100 /* 5x10 characters */

#define LINEAS_5X7 0b00001000 /* 5x7 characters, multiple line */

/* Funciones */

void OpenLCD(unsigned char LcdType) __attribute__ ((section

(".libperi")));

unsigned char BusyLCD(void) __attribute__ ((section (".libperi")));

void WriteCmdLCD(unsigned char cmd) __attribute__ ((section

(".libperi")));

void WriteDataLCD(char data) __attribute__ ((section (".libperi")));

#define putcLCD WriteDataLCD

void putsLCD(char *buffer) __attribute__ ((section (".libperi")));

void GotoxyLCD(unsigned char x,unsigned char y) __attribute__ ((section

(".libperi")));

#endif

/*******************************************************************

\file LCD_30.c

\version: 1.0

\author Suky.

\web www.infopic.comlu.com

\mail [email protected]

\date 18/01/10

********************************************************************/

#include "LCD_C30.h"

unsigned char BusyLCD(void){

#ifdef USE_RW

DATA_PIN_7 = 0;

DATA_PIN_6 = 0;

DATA_PIN_5 = 0;

DATA_PIN_4 = 0;

TRIS_DATA_PIN_7 = 1;

TRIS_DATA_PIN_6 = 1;

Page 52: MPLAB C30 y PIC24

52

TRIS_DATA_PIN_5 = 1;

TRIS_DATA_PIN_4 = 1;

RW_PIN = 1;

RS_PIN = 0;

E_PIN=1;

__delay_us(2);

if(READ_PIN_7==1){

E_PIN = 0;

__delay_us(2);

E_PIN = 1;

__delay_us(2);

E_PIN = 0;

RW_PIN = 0;

return 1;

}else{

E_PIN = 0;

__delay_us(2);

E_PIN = 1;

__delay_us(2);

E_PIN = 0;

RW_PIN = 0;

return 0;

}

#else

__delay_ms(1);

return 0;

#endif

}

void WriteCmdLCD(unsigned char cmd){

#ifdef USE_3PINES

unsigned char i;

unsigned char cmd_temp;

#endif

while(BusyLCD());

#ifndef USE_3PINES

#ifdef USE_RW

RW_PIN = 0;

#endif

RS_PIN = 0;

TRIS_DATA_PIN_7 = 0;

TRIS_DATA_PIN_6 = 0;

TRIS_DATA_PIN_5 = 0;

TRIS_DATA_PIN_4 = 0;

DATA_PIN_7 = (unsigned int)((cmd & 0x80)>>7);

DATA_PIN_6 = (unsigned int)((cmd & 0x40)>>6);

DATA_PIN_5 = (unsigned int)((cmd & 0x20)>>5);

DATA_PIN_4 = (unsigned int)((cmd & 0x10)>>4);

#else

cmd_temp=(cmd>>4); // Rs es bit 4

for(i=0;i<8;i++){

DATA_PIN=(unsigned int)((cmd_temp & 0x80)>>7);

cmd_temp<<=1;

CLOCK_PIN=1;

Page 53: MPLAB C30 y PIC24

53

__delay_us(1);

CLOCK_PIN=0;

}

#endif

Nop();

E_PIN = 1;

__delay_us(2);

E_PIN = 0;

#ifndef USE_3PINES

DATA_PIN_7 = (unsigned int)((cmd & 0x08)>>3);

DATA_PIN_6 = (unsigned int)((cmd & 0x04)>>2);

DATA_PIN_5 = (unsigned int)((cmd & 0x02)>>1);

DATA_PIN_4 = (unsigned int)(cmd & 0x01);

#else

cmd_temp=(cmd&0x0F); // Rs es bit 4

for(i=0;i<8;i++){

DATA_PIN=(unsigned int)((cmd_temp & 0x80)>>7);

cmd_temp<<=1;

CLOCK_PIN=1;

__delay_us(1);

CLOCK_PIN=0;

}

#endif

Nop();

E_PIN = 1;

__delay_us(2);

E_PIN = 0;

}

void OpenLCD(unsigned char LcdType){

char i;

#ifdef USE_3PINES

unsigned char temp;

#endif

__delay_ms(15);

#ifndef USE_3PINES

/* ** Configuración de pines ** */

DATA_PIN_7 = 0;

DATA_PIN_6 = 0;

DATA_PIN_5 = 0;

DATA_PIN_4 = 0;

#ifdef USE_RW

RW_PIN = 0;

#endif

RS_PIN = 0;

E_PIN = 0;

TRIS_DATA_PIN_7 = 0;

TRIS_DATA_PIN_6 = 0;

TRIS_DATA_PIN_5 = 0;

TRIS_DATA_PIN_4 = 0;

#ifdef USE_RW

TRIS_RW = 0;

#endif

TRIS_RS = 0;

TRIS_E = 0;

#else

DATA_PIN = 0;

CLOCK_PIN = 0;

Page 54: MPLAB C30 y PIC24

54

E_PIN = 0;

TRIS_DATA = 0;

TRIS_CLOCK = 0;

TRIS_E = 0;

for(i=0;i<8;i++){

DATA_PIN=0;

CLOCK_PIN=1;

__delay_us(1);

CLOCK_PIN=0;

}

#endif

/* ** INICIALIZACION ** */

#ifndef USE_3PINES

DATA_PIN_5 = 1;

DATA_PIN_4 = 1;

#else

temp=0x03;

for(i=0;i<8;i++){

DATA_PIN=(unsigned int)((temp & 0x80)>>7);

temp<<=1;

CLOCK_PIN=1;

__delay_us(1);

CLOCK_PIN=0;

}

#endif

for(i=0;i<3;i++){

E_PIN = 1;

__delay_ms(2);

E_PIN = 0;

__delay_ms(5);

}

#ifndef USE_3PINES

DATA_PIN_4 = 0;

#else

temp=0x02;

for(i=0;i<8;i++){

DATA_PIN=(unsigned int)((temp & 0x80)>>7);

temp<<=1;

CLOCK_PIN=1;

__delay_us(1);

CLOCK_PIN=0;

}

#endif

E_PIN = 1;

__delay_us(2);

E_PIN = 0;

WriteCmdLCD(0x20 | LcdType); // Tipo display.-

WriteCmdLCD(0x01); // Borramos display.-

WriteCmdLCD(0x06); // Incrementa cursor.-

WriteCmdLCD(0x0C); // Encendemos display.-

}

void WriteDataLCD(char data){

#ifdef USE_3PINES

unsigned char i;

char data_temp;

#endif

Page 55: MPLAB C30 y PIC24

55

while(BusyLCD());

switch(data){

case '\f':

WriteCmdLCD(0x01);

break;

case '\n':

GotoxyLCD(1,2);

break;

default:

#ifndef USE_3PINES

#ifdef USE_RW

RW_PIN = 0;

#endif

RS_PIN = 1;

TRIS_DATA_PIN_7 = 0;

TRIS_DATA_PIN_6 = 0;

TRIS_DATA_PIN_5 = 0;

TRIS_DATA_PIN_4 = 0;

DATA_PIN_7 = (unsigned int)((data & 0x80)>>7);

DATA_PIN_6 = (unsigned int)((data & 0x40)>>6);

DATA_PIN_5 = (unsigned int)((data & 0x20)>>5);

DATA_PIN_4 = (unsigned int)((data & 0x10)>>4);

#else

data_temp=(data>>4); // Rs es bit 4

data_temp|=0x10;

for(i=0;i<8;i++){

DATA_PIN=(unsigned int)((data_temp &

0x80)>>7);

data_temp<<=1;

CLOCK_PIN=1;

__delay_us(1);

CLOCK_PIN=0;

}

#endif

Nop();

E_PIN = 1;

__delay_us(2);

E_PIN = 0;

#ifndef USE_3PINES

DATA_PIN_7 = (unsigned int)((data & 0x08)>>3);

DATA_PIN_6 = (unsigned int)((data & 0x04)>>2);

DATA_PIN_5 = (unsigned int)((data & 0x02)>>1);

DATA_PIN_4 = (unsigned int)(data & 0x01);

#else

data_temp=(data&0x0F); // Rs es bit 4

data_temp|=0x10;

for(i=0;i<8;i++){

DATA_PIN=(unsigned int)((data_temp &

0x80)>>7);

data_temp<<=1;

CLOCK_PIN=1;

__delay_us(1);

CLOCK_PIN=0;

}

#endif

Nop();

E_PIN = 1;

Page 56: MPLAB C30 y PIC24

56

__delay_us(2);

E_PIN = 0;

}

}

void GotoxyLCD(unsigned char x,unsigned char y){

unsigned char Direccion;

if(y!=1){ Direccion=0x40;}

else{ Direccion=0x00;}

Direccion+=x-1;

while(BusyLCD());

WriteCmdLCD(0x80|Direccion);

}

void putsLCD(char *buffer){

while(*buffer != '\0') {

WriteDataLCD(*buffer++);

}

}

Page 57: MPLAB C30 y PIC24

57