Sunteți pe pagina 1din 182

Manual de

Programación

MSP430
Texas Intruments

Presentado por:
Erick Noe Amezquita Lucio
TI MSP430
Acerca de este manual:

Este manual está dedicado a entusiastas en electrónica, con la intención de


proporcionarles los conocimientos necesarios para iniciarse en la
programación, uso y aplicación de los microcontroladores MSP430 de Texas
Instruments (“casi” desde cero). Se abarcaran exclusivamente los contenidos
necesarios para el entendimiento de su funcionamiento, así como se intentará
ser lo más posible breve y conciso (a excepción de esta breve descripción); Sin
embargo, es necesario aclarar que sin algunos conocimientos fuera de la
electrónica no es posible hacer funcionar estos dispositivos, así que se tratara
de abarcar además un poco de esos temas.

Acerca de mí y los µC (microcontroladores):

Seré breve, estudiante de Ing. Electrónica que viene principalmente de usar los
microcontroladores PIC de Microchip (principalmente los de la familia 16FXXXX
y 18FXXXX), particularmente estaba encantado con ellos; sin embargo… por
diferentes circunstancias necesité usar los microcontroladores Texas. El cambio
siempre ha sido un problema pero en este caso es para bien. Los
microcontroladores Texas ofrecen un acercamiento más profundo a lo que es
un µC realmente… principalmente en lo que es el uso de registros. Una vez
aprendida la forma de usarlos se vuelven más cómodos de usar y ofrecen más
control sobre la aplicación que se le dé. No digo que los µC de Microchip no
sean buenos… al contrario, ofrecen una entrada al mundo de los
microcontroladores por su facilidad y generalidad de programación; más
bien… los µC Texas ofrecen una manera más universal de entenderlos a todos
en general… tengo entendido que los AVR se programan con un programa
similar al de Texas por decir un ejemplo. Aun así esta guía estará orientada a
todo entusiasta.
TI MSP430
Índice

Conceptos: _________________________________________________________ 6
µC ________________________________________________________________________ 6

ADC ______________________________________________________________________ 6

PWM ______________________________________________________________________ 6

Puertos____________________________________________________________________ 6

RS232 _____________________________________________________________________ 7

Debugger: ________________________________________________________________ 7

Valores Lógicos: ___________________________________________________________ 7

Compilador: ______________________________________________________________ 7

Sistema Binario:____________________________________________________________ 7

Sistema Hexadecimal: _____________________________________________________ 7

Registros: ___________________________________________________________ 8
Lenguaje C:________________________________________________________ 12
Variables y tipos de dato _________________________________________________ 13

Funciones ________________________________________________________________ 16

Estructura general de un programa y más sintaxis… _______________________ 20

Operadores, condiciones, corrimientos y ‘abreviaciones’… ________________ 22

Arrays (Arreglos) __________________________________________________________ 28

Sentencias condicionales y ciclos finitos e infinitos _________________________ 29

El Entorno IAR: ______________________________________________________ 35


Instalando el IAR Embedded Workbench: _________________________________ 35

Instalando el programador ez430-F2013, ez430-RF2500 ó FET430UIF (JTAG). _ 41

Nota importante. _________________________________________________________ 44

Practica 1: Encendido y apagado de un LED. _____________________________ 46

Practica 2: Encendido y apagado de LED, además de otras cosas… ______ 54

Programación de microcontroladores MSP430: ______________________ 64


Manejo de puertos en MSP430. ___________________________________________ 65
Practica 3: Uso de entradas y salidas en los puertos. ___________________________ 65
TI MSP430

Práctica 4: Uso de algunos ciclos finitos y condicionales + pines individuales de


entrada. _____________________________________________________________________ 67
Practica 5: uso de pines individuales de salida. ________________________________ 69
Manejo de periféricos en MSP430. ________________________________________ 73
ADC (Analogic to Digital Converter) __________________________________________ 73
Practica 6: Uso del ADC. ___________________________________________________ 73
Practica 7: Dos ADCs. ______________________________________________________ 83
Practica 8: El sensor de temperatura interno. ________________________________ 86
Timers (Temporizadores) ______________________________________________________ 90
Práctica 9: PWM (Pulse With Modulation) ____________________________________ 90
Practica 10: PWM calculado, parte 1. _______________________________________ 96
Practica 11: PWM calculado, parte 2 (Mayor frecuencia de operación en el
microcontrolador). _________________________________________________________ 97
Practica 12: PWM calculado, parte 3 (Uso de un cristal externo + configuración
ADC alterna)._____________________________________________________________ 100
Practica 13: Interrupciones por Timers. _____________________________________ 104
Practica 14: Interrupciones por Timers externos. _____________________________ 107
Practica 15: Interrupciones en los puertos. __________________________________ 109
Interfaces. __________________________________________________________________ 116
Practica 16: El estándar RS232-C (para nosotros simplemente RS232). ________ 116
Usando LabVIEW 8.2 (Aunque sirve igual con otras versiones) con la práctica
del termómetro anterior ligeramente modificada. ________________________ 130
Practica 17: El protocolo I 2C. ______________________________________________ 152
Practica 18: El protocolo SPI._______________________________________________ 175
Extra 1. _____________________________________________________________________ 183
Extra 2. _____________________________________________________________________ 183
TI MSP430
Conceptos:
No esperábamos realmente empezar a programar microcontroladores sin algo
de teoría… ¿o sí? He aquí algunas definiciones.

µC
Una forma de abreviar microcontrolador, ahora… ¿Qué es? Básicamente es
como una computadora (mucho menos poderosa claro esta) pero para las
aplicaciones que la queremos es más que suficiente. Tiene su memoria RAM
para trabajar, memoria FLASH para guardar el programa y también puertos de
entrada y salida, ADCs, PWM… etc. Más información normalmente en la hoja
de datos (datasheet) del fabricante.

ADC
Analogic to Digital Converter (Convertidor analógico digital) se usa para
convertir un valor analógico (0 a 5 volts por ejemplo) en uno binario (0’s y 1’s)
para que el µC lo entienda.

PWM
Pulse With Modulation (Modulación por ancho de pulso) es un tren de pulsos
(valores de 0 a 5v por ejemplo) que pueden variar su duración ó ciclo de
trabajo (duty cycle) de tal manera que el tren de pulsos puede formar un
voltaje constante, voltaje nulo o un voltaje promedio. Se usa principalmente
para controlar motores de corriente directa.

Puertos
Normalmente se les llama así a cierta cantidad de pines en un micro que se
usan en conjunto, por ejemplo, los pines denotados en un micro Texas como
P4.0, P4.1, P4.2… P4.7 corresponden al puerto 4 del µC que estamos usando,
que consta de 8 pines en total. Cabe decir que varios de esos pines además
de servir como entradas y salidas algunos de ellos presentan otras funciones
(por ejemplo algunos pueden usarse para comunicación RS232, I2C, SPI…)

Puerto 4 de un µC Texas
Página |7

RS232
De manera breve es un protocolo de comunicación (una manera de
intercambiar datos entre varios dispositivos). Es uno de los más fáciles y mas
ampliamente usados; no es muy rápido, pero su facilidad de uso y velocidad
aceptable lo convierte en una excelente opción para comunicar, por
ejemplo, un µC con una PC.

Debugger:
En español le dicen depurador, (la historia de porque se llama debugger vale
la pena para distraerse un rato: bug). Este generalmente es un programa que
nos ayuda a verificar las rutinas… instrucción por instrucción, en este caso de
un µC para localizar fallas y corregirlas.

Valores Lógicos:
Los valores lógicos siempre han sido 0 y 1, la diferencia radica en que
queramos que sea un 0 y un 1, por ejemplo, para los µC MSP430 de Texas un
cero lógico es 0 volts y para un uno lógico es 3 volts.

Compilador:
Es un programa que se encarga de traducir un lenguaje a otro lenguaje…, el
primero normalmente es un lenguaje que nosotros los mortales podemos
programar con algunas instrucciones entendibles y el segundo por lo regular es
lenguaje maquina… 010101010101 a más no poder.

Sistema Binario:
No esta de más decir que es el que se basa únicamente en 1’s y 0’s…

Sistema Hexadecimal:
Es el que se basa en 16 dígitos… 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F… la calculadora
de Windows es excelente para hacer conversiones entre Hexadecimal,
Decimal y Binario…
TI MSP430
Página |8

Registros:
Ok… a estos les quise dedicar una sección entera puesto que son muy
importantes; básicamente controlan al microcontrolador en sus funciones
fundamentales.

Explicarlo en texto me es algo complicado, así que mencionare su aplicación y


lo empleare en un ejemplo más o menos real (digo más o menos porque faltan
más cosas para hacer lo que describiré).

Supongamos que queremos encender un simple y sencillo LED. Si usamos un


micro Texas hay que manejar un par de registros para llevar a cabo esto.

1.- Necesitamos un puerto, puede ser el que sea, en este caso usaremos el
puerto 1 del µC MSP430F2274

µC MSP430F2274 (o más bien, el encapsulado de este)

2.- Ya escogimos el puerto, ahora seleccionemos un pin, de igual manera


puede ser el que sea, yo escogeré el P1.3.
TI MSP430

Pin 3 del µC
Página |9

3.- Bien, ya sabemos que pin queremos que encienda el LED… ¿Qué hacer?
Pues primero hay que decirle al puerto 1 que el pin 3 va a ser un pin de salida
(es decir, que de el se obtendrán señales 0 y 1 lógicos dependiendo de cómo
lo usemos, su contraparte es que reciba señales de 0 y 1 lógicos)1

Para esto, haremos que hemos leído la hoja de datos (Users Guide de este
micro), y que sabemos que PxDIR es un registro de 8 bits que controla si son
entradas o salidas los pines de un puerto x.

Quiere decir que P1DIR controla los pines del puerto 1 como entradas o salidas.

Para que se den una idea más clara, he aquí una tabla:

P1DIR

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Representación del registro P1DIR2. ¿Notas los 8 espacios? Por eso es un registro
de 8 bits…

Si nosotros quisiéramos el puerto 1 con los 8 pines de entrada, tendríamos que


hacer esto:

P1DIR 0 0 0 0 0 0 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Puerto 1 como solo entrada.

Si nosotros quisiéramos el puerto 1 con los 8 pines de salida, entonces se vería


así:

P1DIR 1 1 1 1 1 1 1 1

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Puerto 1 como solo salida.

Y, si solo quisiéramos como dijimos arriba el pin P1.3 como salida y todos los
demás de entrada… entonces este tiene que quedar así:
TI MSP430

1
Un cero lógico para este micro son 0 volts y un 1 lógico son 3 volts.

2
Para los que usamos PICs esto es un set_tris_a() … considerando a como el puerto 1 y cambiando la
notación, 1 para salidas y 0 para entradas.
P á g i n a | 10

P1DIR 0 0 0 0 1 0 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Pin P1.3 como solo salida y los demás como solo entrada.

4.- ¡Bien! Tenemos un bonito P1.3 como salida… pero por si solo no va a hacer
nada, hay que decirle que por ese pin “saque” un 1 lógico… y para eso, existe
un registro que de manera análoga se encarga de hacer eso, para el puerto 1
este sería así:

P1OUT

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Representación del registro P1OUT3.

Donde si lo llenamos con ceros, a la salida obtendremos 0’s lógicos (0 volts) y si


lo llenamos de 1’s entonces a la salida tendremos unos lógicos (3 volts…
suficientes para prender un LED con su resistencia de 330 Ohms en serie).

Entonces… para prender el LED este quedaría así:

P1OUT 0 0 0 0 1 0 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Pin P1.3 con un 1 lógico y los demás como 0’s lógicos.

¡Sas! Se tiene un 1 lógico a la salida del pin P3.1 listo para prender un LED.

Cabe destacar que si se configura P1DIR y P1OUT:

P1DIR 0 0 0 0 0 0 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Puerto 1 como solo entrada.

P1OUT 0 0 0 0 1 0 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Pin P1.3 con un 1 lógico y los demás como 0’s lógicos.


TI MSP430

3
Adivinaste… los que usamos PIC originalmente asignábamos una variable a la dirección del puerto, algo
así como #byte puertoa=0x05;
P á g i n a | 11

El micro simplemente no sacara un 1 lógico, porque se configuró como


entrada el pin P1.3… (y seguramente tendrás un 0 siempre, porque esta
configurado como entrada) Y si se configura…:

P1DIR 0 0 0 0 1 0 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Puerto 1 como solo entrada.

P1OUT 0 0 0 0 0 1 0 0

P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

Pin P1.3 con un 1 lógico y los demás como 0’s lógicos.

Lo único que vas a lograr ser tener siempre un 0 porque le indicaste al pin
incorrecto que sacara un 1 (así como esta te sacara un 0 simplemente).

¿A qué va todo esto? Si aun no a caído la idea, configurando los registros se


ordena que se quiere que haga el micro, aquí solo fue ordenar al P1.3 que se
comportara como un pin de salida y que mostrara un 1, pero hay muchos más
registros para controlar diversas partes del micro (los que controlan los ADCs,
Temporizadores, PWMs… etc.), que se usan y controlan de la misma manera;
de ahí su fundamental importancia. Si aun así no lo dejo claro, sin ellos
configurados nunca va a funcionar el micro.
TI MSP430
P á g i n a | 12

Lenguaje C:
Bien, seguramente te has preguntado ¿Cómo se llena un registro con 0’s y
1’s...? bien, para eso se utiliza un lenguaje de programación y un compilador.

No entrare en mucho detalle e iré directamente al grano, hay un par de


lenguajes principalmente usados para hacer eso, el ASM (assembler, o
ensamblador) y C. De estos el comúnmente usado es C por ser más amigable
con el usuario al tener instrucciones al estilo DO… WHILE… que significan HAZ…
MIENTRAS… y que literalmente hacen lo que dicen que van a hacer, a
continuación te pongo un ejemplo que no es necesario que entiendas por el
momento:

do //Haz esto…
P1DIR=0x01; //Ponle un 1 al registro P1DIR-Vuelve de solo salida el pin P1.1
P1OUT=0x01; //Ponle un 1 al registro P1OUT-Despliega un 1 en el pin P1.1
while(1); //Mientras “sea verdad” (como es un 1 siempre asume que es
//verdad… es decir, se cicla infinitamente.

Recomiendo ampliamente no poner este ejemplo en un compilador, lo único


que hará será ponerse en ciclo infinito haciendo nada4, lo único que quería
demostrar con éste es el uso de instrucciones con un “significado”; esto
eventualmente nos ayudara mucho a comenzar a programar en C.

Haré un breve tutorial de C con lo más que he usado… (el lenguaje C…


pregúntenle a cualquier programador, es un MUNDO por decir algo,
afortunadamente para programar micros no se usa, o al menos no me ha
tocado saber todo C para hacer lo que quiero…), estas instrucciones se usarán
tal cual en el compilador que veremos más adelante, el IAR Embedded
Workbench.

0.- Comentarios y sintaxis básica

Muchas veces queremos guiarnos agregando notas al código,


particularmente yo lo hago y mucho… las formas mas comunes de ponerlos
son después de dos diagonales ‘//’ ó, si queremos no solo una línea si no todo
un párrafo se usa: ‘/*’ (diagonal asterisco) en la primer línea y ‘*/’ (asterisco
diagonal) en la ultima línea.

Ejemplo 1:

void main(void); //Esto es un comentario…

Ejemplo 2:
TI MSP430

int x;

4
¡Desu!
P á g i n a | 13

void main(void)

{
while(1)

x=1; //Código sin importancia, no lo tienes porque entender.


delay(500);
x=2;

/*
De esta manera
puedes colocar
varias líneas
de comentarios.
*/

Cabe decir que las líneas de comentarios simplemente no son procesadas por
el compilador (y que, en el IAR, una vez que las comentas cambian de color y
se ponen azules tal como en los ejemplos).

Es importante que agregues los ‘;’ al final de cada sentencia (así se les dice a
las líneas de código) solo en casos muy específicos no se pone (esto es muy
común en lenguaje C).

1.- Variables y tipos de dato5


En las variables puedes almacenar números (o caracteres) dependiendo del
tipo de dato.

Los tipos de datos que están en rojo son los más comunes:
TI MSP430

5
Todas se encuentran en este PDF, sección Data Types, pagina 172.
P á g i n a | 14

¿Por qué los más comunes?

Bueno… normalmente no manejamos números muy grandes, si hacemos una


conversión de uno de los ADCs esta es de 10 bits (para algunos micros) y con
un tipo de dato unsigned int nos basta, si no queremos llegar a tanto también
podemos usar char que nos da hasta 8 bits.

Ejemplo 1:

int x; //declaramos que usaremos una variable x…


TI MSP430

x=1234; //le asignamos un numero 1234.


P á g i n a | 15

Ejemplo 2:

int x=20; //declaramos que usaremos una variable x con un valor


//inicial de 20.
x=3456; //le asignamos un numero 3456

El int por si solo se interpreta como un signed int en la mayoría de los


compiladores.

Ejemplo 3:

unsigned int x; //declaramos que usaremos una variable x…

x=3412; //le asignamos un numero 3412.

¿Cuál es la diferencia del signed y el unsigned?: en apariencia son similares,


pero lo que hace el signed es que interpreta la mitad de todos los números
posibles como negativos, por ejemplo, un int a solas es un registro de 16 bits,
esos 16 bits nos dan un rango de 0 a 65535 en decimal, que a su vez quiere
decir que tenemos 65536 números (contando también el cero) en total, si
partimos eso en dos, es decir de 0 a 32767 y de 32768 a 65535; los primeros
32768 números serán interpretados positivos en el rango de 0 a 32767, siendo
los demás interpretados negativos, tomando el rango de -1 a -32768.

Si no has entendido no le tomes mucha importancia, solo asegúrate de que si


vas a usar números negativos usa signed precedido de int o de lo que vallas a
usar y sí no, usa unsigned int ó unsigned más lo que vallas a ocupar. Por
ejemplo, en una operación del tipo:

x=100-200;

Tu resultado será de -100, y si lo colocamos como unsigned int el micro


seguramente terminara haciendo cosas no deseadas, es recomendable
entonces usar entonces el signed int.

Ejemplo 4:

char a=0; //declaramos una variable a con un valor inicial de 0.

a=’d’; //almacenamos la letra d minúscula en la variable a, para almacenar


//caracteres siempre es de regla los apostrofes al final y al principio.

delay(15);

a=120; //almacenamos ahora un numero, en los char puede ser un numero //en el
intervalo de 0 a 255. Para los signed char el intervalo es de -128
TI MSP430

//a 127.

Si requieres de números más grandes quizás necesites un unsigned long ó un


unsigned long long… aunque realmente son números muy grandes, en mi caso
P á g i n a | 16

no me he visto en la necesidad de usarlos (además también están sujetos a la


capacidad del micro).

Ejemplo 5:

float z=0.8;

z=34.56;

Como podrás darte cuenta la diferencia principal de float es que admite


números con punto decimal, estos números son muy útiles como cuando
quieres hacer una medición y te piden tenerla en alguna unidad, dígase
temperatura a grados…

Ejemplo 6:

Int x=4; //declaramos un entero con valor inicial 4


float z=0; //declaramos una variable flotante con valor inicial 0

z=(34.56+x)/3; //el resultado de z será 34.56 más x (en este caso 4) entre 3, esto
//la única manera de guardarlo sin perder los decimales es en
//variable flotante. Si guardáramos el resultado en una variable
//entera lo único que tomaría del número son los números a la
//izquierda del punto decimal.

¿Variables globales ó locales?

Una variable se dice global si esta fuera de una función (normalmente se


encuentran casi hasta arriba del código); entonces, puedes llamar a una
variable global dentro de una o varias funciones cuando tu quieras.

Se dice que una variable es local cuando esta dentro de una función o una
subrutina, no las puedes llamar cuando tú quieras si no exclusivamente dentro
de esa función o esa subrutina (o consecuentes subrutinas).

En el siguiente punto se verá que es una función y más adelante que es una
subrutina.

2.- Funciones
Ok… este es otro apartado grande, seguramente has visto cosas como ‘void
main(void)’ en algunos de los ejemplos de arriba, esa línea de código
representan una función.

Las funciones son relativamente lo primero que el compilador mira, puesto que
dentro de ellas se inserta todo el código que será ejecutado en el micro, en
TI MSP430

particular la función main() que es la primera de todas que viene a ser


ejecutada. Desde la misma puedes ejecutar otras funciones con el nombre
que tú quieras.
P á g i n a | 17

Como comentario breve pero no menos importante mencionaremos la


estructura de una función:

tipo_de_dato función(tipo_de_dato variable)


{
//código…
}

tipo_de_dato se refiere a los tipos como int, unsigned int, char… etc. Además
puede ser void (este ultimo significa vacio).

función puede ser casi el nombre que tu quieras, porque hay funciones
reservadas como por ejemplo main().

variable también pueden ser las que tu quieras, x, y, z… etc.

Las funciones pueden o no recibir valores, así como pueden o no entregar


valores… pero, suficiente plática ha sido ya, procederemos a los ejemplos.

Ejemplo 1:

void main(void) //quizás la función más importante de todas.


{
while(1) //ciclo infinito… o de lo contrario nuestro programa correrá
{ //solo una vez.
x=0;
y=1;
x=1;
y=0;
}
}

En este ejemplo se pone la función principal… siempre, siempre que queramos


ejecutar algún código se pone en esta (y normalmente el código que
queremos que ejecute se coloca en un ciclo infinito como lo es while(1)… ó
while(True)6 se explicará más adelante)

La función de void es: si está antes de la función significa que esta no entrega
valor alguno de regreso, y si está dentro de los paréntesis (argumento) significa
que tampoco recibe nada.

Normalmente la función main() siempre se usa tal cual la acabo de poner (al
ser la principal nunca se ha requerido ni que reciba ni que entregue algo) así
que para futuros programas puedes hacer un copy & paste del mismo.
TI MSP430

6
Esto no siempre compila en el IAR, depende de que micro uses… pero con un simple ‘#Define True 1’
hasta arriba del código se puede arreglar, para este caso esta más que presente en el compilador.
P á g i n a | 18

Ejemplo 2:

int x; //Variable global para que la función la pueda usar aun estando fuera
//de ella.

void multiplica(unsigned int y) //declaramos una función que recibe un valor


{ //que recibe un unsigned int pero no entrega
x=y*2; //nada, lo que sea que reciba lo multiplica por
} //2 y lo coloca en la variable x.

En este ejemplo se puede ver una función cuya única operación es multiplicar
lo que reciba… de esta manera si nosotros colocáramos en una línea de
código:

multiplica(2);

Lo que tendríamos en la variable x seria un bonito 4. Vuelvo a repetir que


pueden recibir varios tipos de variables, como pueden ser char o unsigned
char, float… etc.

Un ejemplo muy bueno y real es este, se usa muy a menudo porque detiene
por un tiempo lo que sea que esté haciendo el micro (más de una ocasión lo
van a usar, así que posteriormente pueden hacer un copy paste del mismo):

Ejemplo 37:

unsigned int delay (unsigned int x) //supuestamente recibimos un valor de la


{ //función y le podemos insertar en ella
//un unsigned int.

unsigned int i,j; //usamos dos variables locales, i y j.


//las cuales solo podrán ser usadas en esta
//función.

for (i = 0; i<= x; i++) //usamos un ciclo for (se explicará más


{ //adelante como se usa)
for(j=0;j<=1000; j++); //otro ciclo for.
}
return 0; //le ‘decimos’ a la función que no entregue
} //nada.

Esta función lo único que hace es contar, contar hasta 1000 y repitiendo el
conteo x veces (ese valor se lo damos nosotros, al estilo de delay(100);) eso
inevitablemente consume recursos del micro y genera un retraso…
normalmente se pone entre líneas de código, para que el micro ‘espere’ un
tiempo antes de ejecutar una segunda instrucción.
TI MSP430

7
Sip… en estos micros no hay una función bonita delay() como lo había en los PICs.
P á g i n a | 19

El return 0; tiene una operación particular, puesto que le dice a la función


delay, que aunque entrega un valor unsigned int este será de cero… es otra
manera de usar void por así decirlo (como si fuera void delay(unsigned int x).

Ejemplo 4:

unsigned int resta(unsigned int y)


{

int z; //variable local.


z=y-200; //le restamos 200 a lo que sea que hallamos recibido en la función.
return z; //entregamos ‘z’ como resultado de la función.

} //fin de la función.

Bien, he aquí nuestra función que entrega y recibe datos… ¿que hace? Bueno
si tú pones unas líneas de código:

int a; //variable global

a=resta(500); //llamamos a la función con un valor que nosotros le hemos


//dado.

En a tendrás un valor de 300, porque invocamos a la función la cual le quitaba


200 al valor que le introdujéramos.

La instrucción return z; se encarga de entregar el valor z, return te entregará lo


que sea que le digas que entregue, puede ser una constante (return 10;), un
flotante (return 10.1;) una variable… etc. Todo depende de cómo declares la
función, si unsigned int resta(), char resta(), float resta()… etc, será elección
tuya conforme a las necesidades que se presenten.

Configúralos acorde al valor que sea, ya que si no seguramente el compilador


te marcará errores y si no lo hace puede que el código no responda como
desees.

Es importante decir que no solo puedes insertarle números… si tu declaras una


variable unsigned int b también la puedes introducir a la función (al estilo de
resta(b); ) y sea el valor que sea b le restará 200. Esto le da más
maniobrabilidad al código.

Nota:

Si tú creas funciones arriba de la función main() entonces no hay problema (la


función main() tiene que ir sí o sí en el código, si no el compilador marcará
TI MSP430

error) pero si lo colocas abajo entonces las cosas cambian:

Ejemplo 5:

int x;
P á g i n a | 20

void multiplica(unsigned int y) // declaramos nuestra función.


{
x=y*2;
}

void main(void)
{
int j;
while(1)
{
multiplica(20);
j=x;
}
}

Aquí la declaración de la función fue antes del main()… pero si nosotros


queremos colocarla después entonces…

Ejemplo 6:

int x;

void multiplica(unsigned int y); //Forzosamente tienes que agregar aquí la


//función o el compilador marcará error.
//la declaras únicamente con la función
//y un ‘;’.

void main(void)
{
int j;
while(1)
{
multiplica(20);
j=x;
}

}void multiplica(unsigned int y) //declaramos nuestra función debajo de main().


{
x=y*2;
}

3.- Estructura general de un programa y más sintaxis…


Con todo lo que hemos visto hasta ahorita ya es posible decir como se
compone un programa básico en IAR y explicar un par de cosas más para
poder inicializarlo.
TI MSP430

Ejemplo 1:

#include "msp430x21x2.h" //Librería correspondiente al micro que queremos.


//Ella contiene todas las rutinas necesarias para
P á g i n a | 21

//hacerlo funcionar, las x pueden ser cualquier


//número o letra, por ejemplo nosotros usaremos el
//micro msp430F2122. Siempre las librerías son
//puestas al principio del código.

#define Verdad 1 //Definimos una constate llamada ‘verdad’ con valor de 1.


//No es regla pero siempre las he visto después de las
//librerías.

#define Falso 0x00 //Otra forma de definir una constante en hexadecimal.


//En IAR la notación hexadecimal se colora de morado
//mientras que la decimal en verde, tu puedes jugar con
//los dos sin ningún problema inclusive en operaciones
//que se verán más abajo.

volatile unsigned int i; //volatile significa que no le reserva a la variable


//una área especifica de memoria en el micro
//no lo tomes mucho en cuenta, también te puedes
//encontrar tipos de dato con el prefijo static que son
//todo lo contrario. Variable global

unsigned int delay (unsigned int x) //esta función ya la habíamos visto


{ //arriba, sirve para crear retraso…
unsigned int i,j; //lo idóneo sería que las funciones
for (i = 0; i<= x; i++) //estuvieran después de las variables
{ //pero eso depende de cada persona.
for(j=0;j<=1000; j++);
}
return 0;
}

void main(void) //Por organización, generalmente cuando se abren corchetes


{ //se escribe debajo de ellos y además se deja un espacio
//razonable, 3 espacios o un ‘Tab’ si lo prefieres.
while(Verdad) //como arriba definimos que Verdad era una constante igual
{ //a 1, El compilador lo toma como si fuera un while(1)
int z; //Variable local.
i=1;
z=5;
delay(100);
i=0;
z=3;
delay(100);
}
}

Como se puede observar introdujimos un par de términos más:


TI MSP430

#include “msp430x21x2.h"

Este comando lo que hace es obtener de otro archivo las definiciones de


constantes; muchas de ellas son para los registros del micro, aunque se
P á g i n a | 22

pueden invocar a más librerías como math.h que contiene algunas funciones
matemáticas como la de sen x.

#define Verdad 1
#define Falso 0x01

#define lo que hace es declarar constantes, así como escribí ‘Verdad’ también
puede ser cualquier cosa, y no esta restringido a una constante, puedes poner
tantos #define como desees. Otra cosa, cuando se coloca un numero al estilo
de ‘0x00’ quiere decir un cero en hexadecimal… hay muchos programas
donde se usa el formato hexadecimal para las constantes o variables, algunos
otros ejemplos son 0x01, 0xAA, 0xFF… etc.

4.- Operadores, condiciones, corrimientos y ‘abreviaciones’…


Los operadores se usan principalmente para, valga la redundancia, hacer
operaciones, normalmente entre números o variables. Se dividen en dos tipos:
Lógicos y Aritméticos.

Operadores Lógicos:

Primero una breve teoría… aquellos que están familiarizados con la electrónica
los recuerdan muy bien, son principalmente AND, OR y NOT, (que vienen a ser
multiplicación, suma y negación) una tabla de verdad ayuda a ver como
funcionan estos operadores.

Entrada Entrada AND OR XOR


A B (Multiplicación) (Suma) (OR
Salida C Salida Especial)
C Salida C

0 0 0 0 0

0 1 0 1 1

1 0 0 1 1

1 1 1 1 0

Entrada NOT
A (Negación)
Salida B
TI MSP430

0 1

1 0
P á g i n a | 23

Creo que de todas quizás la más confusa sea la XOR, pero si lo quieres ver de
una forma fácil, la XOR compara dos entradas y si las dos son iguales a la
salida obtienes un 0, de lo contrario es 1.

Recuerda que si tienes un numero con empezando con un ‘0x’ se trata de un


numero hexadecimal (es bueno acostumbrarse a esta notación porque es
muy usada) el rango es desde 0 hasta F.

Recomiendo mucho que usen la calculadora de Windows (modo científico) y


escriban el número en hexadecimal, entonces convertirlo a binario e
imaginarse la operación bit por bit.

Su notación en C(IAR8):

AND OR NOT XOR

Símbolo & | ~ ^

Ejemplo 1: x=0xFF&0x01; x=0x03|0x04; x=~0x05;9 x=0xFF^0x0F;


Operación entre 2
números.

Ejemplo 2: x=y&z; x=y|z; x=~z; x=y^z;


Operación entre 2
variables.

Ejemplo 3: x &= 0xFF; x |= 0xF0; x = ~0x03; x ^= 0x0F;


Operación entre la
x &= y; x |= y; x = ~y; x ^= y;
misma variable que
lo almacena y un
numero ó variable.

Ejemplo 4: x=0xFF&y; x=0x03|y; Nota: solo x=0xFF^0x0F;


Operación entre tiene una
entrada.
variables y números.

Los operadores lógicos son muy usados puesto que casi toda nuestra
interacción con el micro es en bits, dígase los puertos por ejemplo, tienen pines
y cada uno de ellos para nosotros es un bit.
TI MSP430

8
En los PICs, que normalmente usamos PIC C Compiler… la notación es algo diferente.

9
Los NOT solo tienen una entrada…
P á g i n a | 24

Operadores Aritméticos:

Estos son los de toda la vida… son muy usados a la hora de convertir resultados
provenientes del ADC por decir un ejemplo.

Suma Resta Multiplicación División

Símbolo + - * /

Ejemplo 1: x=3+0x10; x=4-0x05; x=3*0x02; x=10/0x02;


operación entre
x=3+y; x=y-0x0A; x=4*z; x=3/a;
dos números o
variables.

Habrás notado que mezclo números decimales y hexadecimales, esto se


puede hacer a entera voluntad y no solo entre dos números, puedes poner
más de acuerdo a la operación que necesites.

Condiciones:

Estas funcionan en conjunto con sentencias condicionales como el IF, WHILE,


FOR… etc. (y estos se explicarán mas adelante) aplican tanto para números
como para variables… aunque normalmente comparamos una variable con
un numero.

Igual Mayor Mayor Menor Menor Diferente


a que igual que igual que de
que

Símbolo == > >= < <= !=

Ejemplo 1: x==3; x>0x10; x>=0x0F; x<4; x<=(3*0x02); x!=0;


Comparación
x==y; x>y; x>=y; x<y; x<=(4*z); x!=(3/a);
entre dos
números ó
variables.

Notaras que en algunas comparaciones incluí algunas operaciones, eso es


también valido.

Corrimientos:
TI MSP430

Bien, hemos llegado a una parte que es mas ‘truco’ que otra cosa… los
corrimientos responden a una pregunta que me hice mucho tiempo:

¿Cómo hacer que un registro de 16 bits se parta en dos de 8?


P á g i n a | 25

Pues bien… aquí entran los corrimientos.

Corrimiento Corrimiento
a la a la
derecha izquierda

Símbolo >> <<

Ejemplo x>>8; x<<8;

Corremos 8 Corremos 8
espacios a espacios a
la derecha la izquierda
un registro un registro

¿Y esto para que sirve?

Bueno… volvamos a algo más de teoría. Supongamos que nosotros tenemos


un unsigned int x donde almacenamos un numero muy grande, supongamos…
25123 en decimal que en binario vendría siendo 0110001000100011 (recordar
que unsigned int es de 16 bits). Trataré de hacer un programa e irlo explicando
sobre la marcha:
TI MSP430
P á g i n a | 26

unsigned int x,w; //variables x y w globales, enteros de 16 bits sin signo.


unsigned char y,z; //declaramos dos variables globales de 8 bits sin signo.

x=25123; //asignamos un valor enorme a x.

x 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1

215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20

Registro interno de nuestra variable x con un valor 25123 decimal.

y=x&0xFF; //le decimos que multiplique por 1 los primeros 8 bits y que los
//almacene en y. Es decir:

x 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1

215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20

& Operación AND con 0xFF (000000001111111) en binario

0xFF 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

27 26 25 24 23 22 21 20

= 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1

Resultado almacenado en y: 0 0 1 0 0 0 1 1

//y solo puede tomar 8 bits por ser unsigned char. A esto se le dice
//almacenar la parte baja de x en y (los primeros 8 bits). Ahora vamos con la
//parte alta (los últimos 8 bits).

z=(x>>8)&0xFF; //le decimos que corra (desplace) 8 espacios a la derecha el


//registro de la variable x, después de eso que multiplique por 1
//los primeros 8 bits y que los almacene en z. Es decir:
TI MSP430
P á g i n a | 27

x 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1

215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20

>>8 Corrimiento 8 bits a la derecha

0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0

Hemos corrido los bits más altos. 215 214 213 212 211 210 29 28

& Operación AND con 0xFF (000000001111111) en binario

0xFF 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

= 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0

Notación real ya con los bits corridos 27 26 25 24 23 22 21 20

Resultado almacenado en z: 0 1 1 0 0 0 1 0

//Listo, ahora tanto y como z poseen la parte alta y baja respectivamente del
//dato original de 16 bits, que queramos hacer con ellos es nuestra elección.
//Yo particularmente lo he usado mucho para poder enviar un dato grande
//dividido en dos pedazos por dos puertos (8 pines cada 1 sumando 16 pines
//en total) o para mandar datos por rs232 pero eso se verá mas adelante.

//La pregunta de ahora es como volverlos a unir… eso no es muy difícil.

w= y + (z<<8); //usamos nuestra otra variable unsigned int y le sumamos la


//parte baja tal cual (y)+ la parte alta (z) corrida 8 espacios a la
//izquierda. Formando nuestro numero original.

El uso de los corrimientos depende de la imaginación de cada quien, pueden


tener muchos usos.

Abreviaciones:

Este segmento será corto. Las abreviaciones principalmente se usan para


secuencias muy conocidas, por ejemplo cuando en un conteo a una variable
TI MSP430

solo le queremos sumar un 1 consecutivamente de tal manera que su valor


valla incrementándose… (0, 1, 2, 3… 100, 101, 102… etc.) O disminuir de igual
manera. He aquí los que más he visto.
P á g i n a | 28

unsigned int i;
unsigned int s;

i++; //Esto es precisamente para incrementar cada vez que se toque esta
//instrucción el valor de i en 1.

s--; //Y esto otro disminuye el valor de s en 1 cada vez que es ejecutada esta
//instrucción.

5.- Arrays (Arreglos)


Para ser breves, los Arrays son arreglos de variables… pueden ser
unidimensionales, bidimensionales… tridimensionales… en fin, cosas realmente
grandes. Hasta donde sé, en C se llegan a usar arreglos grandes y de varias
dimensiones (y cuando no punteros, pero esos son otra historia), pero en este
caso me limitare a describir los unidimensionales.

unsigned char a[4]; //Arreglo con 4 ‘espacios’…

a[0]=’h’; //recuerda que en los char también puedes almacenar caracteres…


a[1]=’o’; //de hecho, char es la abreviación de character… o letra para
a[2]=’l’; //nosotros.
a[3]=’a’;

Otro ejemplo:

unsigned char t[3]; //Arreglo con 3 ‘espacios’…

t[0]=10;
t[1]=100;
t[2]=124;

Como te podrás dar cuenta, en ‘una’ variable hemos almacenado varios


valores. Los puedes llamar a voluntad simplemente invocando a[x] donde x
puede ser de 0 a n-1 dependiendo de que hayamos colocado en unsigned
char a[n];

Por ejemplo (siguiendo el primer ejemplo de arriba):

unsigned char x;
unsigned char y;
unsigned char z;
unsigned char w;

x=a[0];
y=a[1];
z=a[2];
w=a[3];
TI MSP430

Algunos de los programas que se verán en la sección de IAR harán uso de ellos
de una manera muy útil es por eso la mención de los mismos.
P á g i n a | 29

6.- Sentencias condicionales y ciclos finitos e infinitos


Finalmente llego a la parte que llevo diciendo que iba a explicar. Estas
sentencias se puede decir que son de las más importantes… puesto que las
operaciones que hacen son de ciclado o de selección.

Ciclos infinitos:

Normalmente los usamos para ciclar indefinidamente nuestro programa,


suelen estar dentro de la función main()

Ejemplo 1:

void main (void)


{
while(1)
{
//Codigo.
}
}

Ejemplo 2:

void main (void)


{
while(True)
{
//Codigo.
}
}

True en la mayoría de las ocasiones esta definido como 1 en la librería del


micro a usar, para los MSP430 es relativamente más común usar 1 10. A mí en
particular en una ocasión cuando intente programar un MSP430F2611 no me
quiso compilar con while(True) así que lo deje 1 para todos.

Ejemplo 3:

void main (void)


{
for(;;)
{
//Código.
}
}

for sin argumento alguno en los paréntesis también se comporta como un ciclo
TI MSP430

infinito. Es importante tener en cuenta que se debe de evitar el uso de muchos

10
En los PICs era de ley siempre el TRUE.
P á g i n a | 30

ciclos infinitos ya que pueden bloquear al micro (al igual que las PCs también
se bloquean). La única manera de salirse de los ciclos infinitos es con la
instrucción break; que literalmente ‘rompe’ el ciclo.

Condicionales:

Sentencia if

El if básicamente nos permite comparar y ejecutar dos opciones en base al


resultado de la comparación (la traducción de if es un si condicional).

Ejemplo 1:

unsigned int x=1;

void main(void)
{
while(1)
{
x=x+1; //incrementa el valor de x en 1… también pude haber puesto ‘x++;’
if(x>=10) //Si x es igual a 5…
{
break;
}
}
}

Este programa incrementa el valor de x (comenzando en 1) hasta llegar a diez.


Cuando el if ‘detecta’ que x es mayor o igual a 10 entonces ejecuta el código
que tiene dentro de si, un break; que termina todo el ciclo.

El uso más común de if es el de comparar, aunque también se le puede


agregar otras instrucciones que lo hacen más eficiente como else (de
cualquier manera) o else if (de cualquier manera si…)

Ejemplo 2:

unsigned int x=0;

void main(void)
{
for(;;) //Ciclo infinito..
{
if(x==10) //Si x es igual a 10…
{
x=15; //Iguala a x con 15…
}
else if(x>=20) //De cualquier manera si x es igual o mayor que 20… en estos if se usan
TI MSP430

{ //mucho las condiciones descritas más arriba.


break; //Salir del ciclo infinito
}
Else //De cualquier manera
{
P á g i n a | 31

x++; //Incrementa x en 1.
}
}
}

Este programa lo que hace es incrementar x en 1 (empezando por 0), cuando


x es igual a 10 (primer if) entonces iguala x con 15 y sigue incrementándose en
1, cuando finalmente x es mayor igual a 20 entonces llama al else if el cual se
encarga de salirse del ciclo infinito.

En resumen else básicamente es cualquier caso de x exceptuando if y else if,


es necesario mencionar que else if es una condición que se llama solo si la
primera condición if no se cumple. Puede haber varios else if anidados a un if
como una manera de decir ‘y si no esto… y si eso no pues esto… y si no…’ etc.
Y solo un else denotando ‘y si nada de lo anterior se cumple… esto’. En casi
todos los programas es común colocar varios if, podría decirse que es uno de
los comandos más útiles.

Sentencia Switch y Case

Más de una ocasión nos vamos a encontrar con que requerimos colocar
muchos if’s creando un código poco legible, es entonces cuando se usa
switch y case:

Ejemplo 1:

unsigned int c;
unsigned int action;

void main(void)
{
while(1)
{
c++; //incremento en 1 con cada ciclo de programa…
}
switch(c) //uso switch con la variable c por argumento…
{
case 1: //en caso de que c=1 entonces
action = 1; //dale a la variable action un valor de 1.
break;
case 2: //en caso de que c=2 entonces
action = 2; //dale a la variable action un valor de 2… etc.
break;
case 3:
action = 3;
break;
case 4:
action = 4;
TI MSP430

break;
case 5:
action = 5;
break;
case 6:
action = 6;
P á g i n a | 32

break;
default: //Default vendría a ser el else a secas. Si no encuentra una condición
action = 7; //definida para c entonces usa esta, que le da el valor a action de 7.
break;
}
}
}

Como se puede ver cada que la variable c se incrementa esta selecciona una
opción posible de switch. Siendo los cases if’s que nos permiten hacer una
selección diferente. default: se comporta exactamente igual que else, de
manera que si no encuentra una un case que corresponda a alguna opción
que la variable c envíe automáticamente se toma la opción contenida en
default: es importante colocar un break; cuando terminemos de insertar
código en el case: o de lo contrario no marcaremos su fin (y el compilador
dará error).

Ciclos Finitos:

Algunas veces se nos presentara la situación que queramos hacer un


promedio, un conteo o simplemente una secuencia que no se requiere que
sea infinita… para eso se requieren ciclos finitos.

Sentencia for

Esta es la más común para conteos… ya que puedes tomar la variable con la
cual la misma sentencia está contando (tienes que declararla previamente en
el programa). Esta es su sintaxis:

for(numero inicial; condición de conteo; incremento)


{
//codigo
}

Un poco más entendible podría ser:

unsigned int i; //casi siempre escogen i ó j para los conteos…

for(i=0;i<=10;i++) //Esto quiere decir… (Comienza a contar desde 0; mientras sea menor igual
{ //a 10; incrementa el conteo de 1 en 1.
//Codigo…
}

Ejemplo 1:

unsigned int i; //casi siempre escogen i ó j para los conteos…


TI MSP430

unsigned int z=0;

void main(void)
{
for(i=3;i<=20;i++) //Esto quiere decir… (Comienza a contar desde 3; mientras sea menor
{ //igual a 20 con incrementos de 1)
P á g i n a | 33

z=z+i; //Incrementa el valor de z en z+i (como si hiciéramos una sumatoria, z


} //tomará valores de (0+3,3+4,7+5,12+6,18+7... etc.)
}

Ejemplo 2:

unsigned int i; //casi siempre escogen i ó j para los conteos…


unsigned int z=0;

void main(void)
{
for(i=2;i<=10;i=i+2) //Esto quiere decir… (Comienza a contar desde 2; mientras sea menor
{ //igual a 10 con incrementos de 2)
z=z+i; //Incrementa el valor de z en z+i (como si hiciéramos una sumatoria, z
} //tomará valores de (0+2,2+4,6+8 y 10+10)
}

La única diferencia de este programa y el anterior son los incrementos y el


valor de inicio, se puede jugar con ellos a voluntad para resolver alguna
necesidad en particular.

Sentencia while

while significa ‘mientras’ en español, repetirá el ciclo de lo que sea que haga
mientras se cumpla una condición en particular.

Ejemplo 1:

unsigned int j; //casi siempre escogen i ó j para los conteos…

void main(void)
{
while(j<=10) //una vez entrado en este ciclo no se saldrá de él a menos que la condicion
{ //que se le dio de ciclado se rompa, es decir, hasta que j sea 11.
j++; //lo que hará es ejecutar el incremento de j hasta que este alcance el 11,
} //entonces se incumplirá la condición.
}

Ejemplo 2

unsigned int j=5; //casi siempre escogen i ó j para los conteos…

void main(void)
{
while( (j>=5) | (j<=10) ) //ahora se cicla de acuerdo a dos condiciones, si j>=5 ó j<=10.
{ //como declaramos con un valor inicial j=5 entonces se cumplirá la condición
j++; //para entrar en el ciclo.
TI MSP430

}
}

En este ejemplo hice uso de varias condiciones a la vez, Se puede poner varias
condiciones de acuerdo a la necesidad que se presente; por ejemplo:
P á g i n a | 34

Si requerimos que una U otra condición se cumpla se utiliza un OR:


(condición 1)|(condición 2)

Si requerimos que todas las condiciones se cumplan se usa un AND:


(condición 1)&(condicion2)

Si requerimos de una condición sea exactamente lo contrario de algo,


entonces se usa un NOT11: !(condición)

Si queremos usar una mezcla de condiciones también se puede…


((condición 1)|(condición 2))&((condición 3)|(condición 4))12

Las condiciones se pueden usar en los if’s, case’s, y while’s por igual.

Sentencia do + while

Hay una variación un poco interesante del while, el do con el while:

Ejemplo 1:

unsigned int j=0,x=0;

void main(void)
{
while(1)
{
do
{
j++;
}while( !( (j<=5) | (j>=10) ) ); //Condición ‘Mientras j sea diferente de <=5 ó >=10’
x++;
}
}

Trataré de explicarlo con una tabla:

Iteración Operación del programa x J

1 Los dos cuentan 1 1

2 Los dos cuentan 2 2

3 Los dos cuentan 3 3

4 Los dos cuentan 4 4


TI MSP430

11
El not como operador es ~, pero para condiciones se usa el !.

12
Esto se traduciría como, ‘si la condición 1 ó la condición 2 y la condición 3 ó la condición 4’ se cumplen
entonces haz algo.
P á g i n a | 35

5 Los dos cuentan, entra en la condición de do while 5 5

6 Ciclo do while 5 6

7 Ciclo do while 5 7

8 Ciclo do while 5 8

9 Ciclo do while 5 9

10 La condición do while se deja de cumplir 5 10

11 Los dos cuentan 6 11

12 Los dos cuentan 7 12

La diferencia entre un while y un do while es que mientras el while una vez


entrado en el ciclo este no deja de funcionar hasta que se rompa la
condición, y en el do while el código dentro del mismo funciona a la par con
el programa, siempre y cuando no se cumpla la condición puesta en el mismo.
Una vez que llegue a la condición establecida el único código que se
ejecutará será el que esta dentro del ciclo, y una vez que se termine la
condición nuevamente el código del ciclo tanto el programa funcionaran a la
par.

Con esto el tutorial básico de Lenguaje C se termina. Era necesario que al


menos se conocieran todos los comandos de arriba para que los programas
para programar los microcontroladores no parecieran jeroglíficos.

Hay algunas otras instrucciones más puntuales que no mencione aquí pero a
medida que vallamos programando en IAR las iré explicando.

El Entorno IAR:

Instalando el IAR Embedded Workbench:


Bien, teniendo ya los conocimientos básicos es posible adentrarnos a nuestros
primeros pasos para programar, pero primero lo primero… Tenemos que
instalar el compilador en nuestra computadora.

Existen dos versiones gratuitas del IAR Embedded Workbench, la Kickstart que
nos permite hasta 4 Kilobytes de código (muchas veces más que suficiente
TI MSP430
P á g i n a | 36

para lo que queremos hacer) y, la versión de evaluación, que permite usar el


compilador sin restricción alguna… por 30 días13.

Para obtener ambas versiones es necesario entrar en la página de IAR y


registrarse, pero para hacer las cosas más rápidas aquí está la versión Kickstart:

IAR Embedded Workbench (Versión Kickstart)

El programa les pedirá dos números los cuales son estos:

License Number:

9540-844-764-6148

License Key:

O0PLK9HW4DPFBVT5X64Z4DN2FEVYDNR5RRZPRRHJEV9W81EKWKESWXYWKW6M
NSVQN3ZEQ7SM7JTZ5GPFDI7JCAPK2KBWI5N90OODJOHI3ZE0UBCLVAA0F0IOB2S
M2002HESUSC0YHYU0HSKI0P5RHVRCTTJVD1LMOARVSF3QZT0LCDMAMHP2VSIALJ
4C# Feature: EW430-KS4 Version: 01_WIN Temporary license (Licno:9540-844-
764-6148), expires 2033-07-23

Una vez instalado tendremos algo como esto:

Es entonces tiempo de proceder a crear nuestros programas.


TI MSP430

13
Para aquellos que están puestos en este mundo de las PCs, a lo mejor está de más decirlo pero
pueden instalar una maquina virtual, instalar el IAR en su versión de evaluación y de ahí usarlo
indefinidamente reiniciando la maquina virtual cuantas veces sea necesario…
P á g i n a | 37

Para empezar a crear un nuevo proyecto seleccionar la primera opción


Create a new project in current workspace… y obtendremos un cuadro de
dialogo como este:

Expandimos C y seleccionamos main entonces presionamos en OK. Les


aparecerá un cuadro de dialogo pidiéndoles donde quieren guardar su recién
creado proyecto, pueden ponerle el nombre que más les convenga:
TI MSP430
P á g i n a | 38

Presionamos en save y veremos una pantalla con algo de código ya puesto…


entonces presionamos con el botón derecho del mouse sobre test1 – Deb…
(Donde test1 realmente puede ser el nombre con el que hallas guardado
anteriormente) y entonces presionamos options:

Nos aparecerá un cuadro de dialogo con bastantes opciones, lo primero que


haremos es seleccionar que microcontrolador usaremos, presiona el botón 1,
entonces aparecerá un menú desplegable 2 donde podrás seleccionar la
familia y finalmente saldrá otro menú desplegable 3 donde podrás seleccionar
el microcontrolador a usar, nosotros usaremos el MSP430F2132. Después de
haber realizado eso presiona OK:
TI MSP430
P á g i n a | 39

Volviendo al código, en la parte donde está escrito #include "io430.h", si no


cambio a #include "msp430x21x2.h", entonces cámbialo simplemente
escribiendo la sentencia anterior, de lo contrario es muy plausible que nos de
errores a la hora de programar el microcontrolador, hazlo de tal manera que
quede así:
TI MSP430

Eso en el caso del microcontrolador MSP430F2132, si tienes otro


microcontrolador lo recomendable es fijarse en el directorio C:\Archivos de
P á g i n a | 40

Programa\IAR Systems\Embedded Workbench 5.0\430\inc y observar el


archivo que más se parezca al microcontrolador que se use, por ejemplo:

Que viene a ser el microcontrolador mencionado arriba. Hecho esto, vuelve a


presionar con el botón derecho del mouse sobre test1 – Deb… colócate en la
opción Debugger y en el recuadro Driver seleccionar FET Debugger, esto es
para que el programa use nuestro programador, si lo dejamos en Simulator no
hará nada más que correr el código que hagamos en el mismo programa y lo
simulará desde ahí. Finalmente presiona OK.
TI MSP430
P á g i n a | 41

Esto te regresara nuevamente al código. Presiona el icono para guardar.

Instalando el programador ez430-F2013, ez430-RF2500 ó


FET430UIF (JTAG).
Olvidaba mencionar que los programadores no son exactamente Plug&Play…
así que aquí va una breve guía de cómo instalarlos.

1.- Conecta tu programador y espera a que el globo Se ha encontrado un


nuevo Hardware aparezca y a que la ventana de Bienvenido al asistente de
hardware nuevo se muestre (Las imágenes son de un SO en inglés, pero en
realidad es exactamente lo mismo en español):
TI MSP430
P á g i n a | 42

Presiona siguiente, después te aparecerá una pantalla donde indica si quieres


buscar los drivers en línea o no, selecciona No esta vez y presiona siguiente:

Después selecciona Instalar software desde una ubicación específica:


TI MSP430
P á g i n a | 43

En la siguiente pantalla les mostrara opciones para localizar el driver, da la


casualidad de que el driver que necesitamos lo instala el mismo IAR
Embedded Workbench. Marcamos la palomita en 1 (incluir esta locación en la
búsqueda), y damos clic en Buscar (2), navegamos en las carpetas hasta
encontrar 3 (Normalmente es:

C:\Archivos de programa\IAR Systems\Embedded Workbench


5.0\430\drivers\TISUSBFET\WinXP), presionamos OK (4) y damos siguiente (5).

Hecho esto lo que resta es solamente siguiente, siguiente, siguiente 14. Les
volverá a mostrar de nueva cuenta el asistente de nuevo hardware
encontrado, esto es porque en si el programador es un puerto serial y
programador a la vez (2 en 1, pero el puerto serial solo se puede usar de serie
en el ez430-RF2500 y en el ez430-RFF2013 con modificación previa de la
EEPROM usada por el TUSB3410, se explicara más adelante el proceso para
ello).

14
Es posible que Windows XP, Vista (32 Bits) les muestre advertencias de que el hardware no ha sido
TI MSP430

firmado y si se desea continuar. La respuesta es darle que si o de lo contrario no se instalara el driver del
programador. Además, Aquí estoy usando XP pero puedo corroborar que en Vista (32 Bits) también
funciona (Hay veces que IAR muestra “Parallel tools can’t be used on Windows vista” pero basta con
desconectar y reconectar el programador para que este funcione.)
P á g i n a | 44

Nota importante.15
En algunas pruebas que he realizado he comprobado que en las HP de la serie
DVXXXX el programador NO FUNCIONA (Hablando en especifico, el
programador del paquete ez430-RF2500). ¿Solución? Hay que deshabilitar (y
en un caso desinstalar) el driver del control remoto de HP, lo he comprobado
yo en tres maquinas HP y en efecto ese es el problema (ni idea del por qué se
interfieren el uno al otro)… para ello, posiciónate sobre el icono de Mi PC, clic
derecho del mouse y darle propiedades:

Entonces en la pestaña de Hardware casi el primer botón, seleccionar


Administrador de dispositivos:
TI MSP430

15
Más información en esta página de la Texas Instruments:
https://community.ti.com/forums/t/329.aspx
P á g i n a | 45

Ya en el administrador de dispositivos, colócate en el menú desplegable de


Dispositivos de interface humana:
TI MSP430
P á g i n a | 46

Mi laptop es una Dell… así que no puedo mostrarles exactamente que es, pero
básicamente el que se tiene que desinstalar es HP Remote Control (selecciona
que no quieres reiniciar aun) y otro que mencione algo similar (algo parecido a
compatible con control remoto HID) este deshabilítalo y reinicia. Si te pide que
instales drivers para nuevo hardware encontrado simplemente dale cancelar.
Vuelve a abrir el administrador de dispositivos y seguramente habrá un
componente con un signo de interrogación amarillo, ahora si deshabilítalo y
reinicia. Solo entonces te dejara usar el programador (Al parecer el ez430-
F2013 no tiene estos problemas, pero este programador no tiene interface USB-
RS232 Incluida de serie). Esto fue probado en Vista (32 Bits) con éxito.

Para aquellos que tienen XP el problema parece ser con el Quick Launch
Buttons, de igual manera hay que deshabilitar y/o desinstalar para que deje
usar el programador.

Regresando a la pantalla de código del IAR Embedded Workbench es tiempo


de crear nuestro primer programa básico… el Hola mundo16 de todo
microcontrolador.

Practica 1: Encendido y apagado de un LED.


El título lo dice todo. Procederemos a encender este pin:

Del microcontrolador MSP430F2132. En el código, teclearemos solo las


instrucciones a la derecha, los comentarios… bueno, los pongo para guiar
principalmente.
TI MSP430

//Programa para encender un LED por el puerto 1, pin 1 (P1.0)

16
http://es.wikipedia.org/wiki/Hola_mundo Hello World, recomiendo leerlo para entretenerse un rato.
P á g i n a | 47

#include "msp430x21x2.h"

int main( void ) //Aquí siempre empezaremos nuestro código, como nuestra única
//intención es demostrar el uso del IAR no nos meteremos con
//mucho aun.
{
// Stop watchdog timer to prevent time out reset <- esto siempre lo agregan.
WDTCTL = WDTPW + WDTHOLD; //<- Igual esta instrucción, su función es actuar
//como watchdog, un watchdog resetea el micro
//si no se le da una señal que indique que
//el proceso esta funcionando, muy útil cuando se
//tiene un programa que se traba mucho.
//Son computadoras después de todo y estas también
//Se quedan pasmadas.
P1DIR=0xFF; //<- Aquí le decimos al microcontrolador que todo el puerto 1 será
//de solo salida, como queremos prender un led rápido esta es una
//opción viable siempre y cuando no se quieran conectar entradas a
//este puerto, díganse botones, pushbutons... etc. Noten la
//nomenclatura hexadecimal, es de lo más común en esta programación
//de micros... una forma fácil de ver esto en formato de pines es
//así: F hexadecimal= 15 en decimal=1111 en binario (la calculadora de
//Windows es excelente para esto (modo científica)
//Entonces F F = 1111 1111.
P1OUT=0x01; //La instrucción por si sola se explica, manda un 1 a la salida.
return 0; //El int main al haber sido declarado de esa manera, se recomienda
//colocar esta instrucción para evitar problemas (como se vio
//anteriormente la función busca regresar un valor, así que... que
//mejor que el 0 cuando no se quiere hacer nada.
}

Una vez hecho esto, presiona Ctrl+D este comando indica a IAR que compile
el programa y a su vez, si tienes un programador conectado (y un micro que
programar) que inyecte el código al microcontrolador. Te aparecerá un
cuadro de dialogo como el siguiente:
TI MSP430
P á g i n a | 48

Dale el nombre que quieras (yo le puse test1 de nueva cuenta) y guarda.
Aparecerán diálogos con barras de estado que desaparecen tan rápido
como aparecieron (si hay algún error en el programador o el micro te mostrará
un error indicando que o no existe programador o no puede encontrar al
micro). Si todo sale bien, pasaras a la siguiente pantalla.
TI MSP430
P á g i n a | 49

Y a esta misma se le conoce como Debugger. Quizás una de las herramientas


más poderosas que haya probado17 puesto que te permite ver, paso por paso
ver como tu código es ejecutado… y además, te muestra las variables que
intervienen en el proceso.

Recordaras los registros P1OUT y P1DIR… hay una forma fácil de apreciarlos en
este Debugger. Selecciona View en el menú principal y después de eso
selecciona Register…
TI MSP430

17
En los PICs de microchip siempre quise algo así.
P á g i n a | 50

Una vez hecho eso a la derecha te habrá salido otra pequeña sección similar
a esta:

Queremos ver acción, así que en el menú desplegable donde menciona CPU
Registers selecciona P1/2:
TI MSP430
P á g i n a | 51

Hecho esto veras nombres algo conocidos, son los registros de entrada y salida
(del puerto 1 como del puerto 2), expande P1OUT y P1DIR (y… si quieres cierra
la ventana que está a un costado, Disassembly, no la usaremos y así liberamos
espacio para poder expandir la ventana de Registers):

Todo está en ceros, si no lo está es posible que el micro haya tenido un


programa anteriormente. Ahora, la parte interesante, presiona lenta y
continuamente y observa como el programa avanza instrucción por
TI MSP430

instrucción y no solo eso, además IAR te permite ver que está pasando en los
registros del microcontrolador como lo muestran las siguientes capturas:
P á g i n a | 52
TI MSP430
P á g i n a | 53

La siguiente vez que presiones IAR se saldrá del modo debug dejando la
pantalla como en un principio, esperando a que nuevamente presionemos
, esto es porque no ciclamos indefinidamente el programa, que para este
sencillo ejemplo realmente no es necesario. Como buena costumbre
informática, presiona para guardar todo y después presiona para salir
del modo debugger totalmente.

IAR permite tener varios programas en un solo proyecto, así que procederemos
TI MSP430

a crear otro, esta vez alternaremos el encendido y apagado del LED…


P á g i n a | 54

Practica 2: Encendido y apagado de LED, además de otras


cosas…
Selecciona Project y después selecciona Create New Project:

Te volverá a salir una pantalla conocida:


TI MSP430
P á g i n a | 55

Esta vez selecciona Empty project y guarda con un nombre diferente… yo usé
test2. Una vez se haya realizado esto la ventana izquierda donde están los
proyectos lucirá así:

Agregando las pestañas de Overview, test1 y test2. Ahora, donde sea que se
hallan guardado los archivos abriremos esa carpeta y haremos una copia de
main.c, puede ser renombrada a gusto, en mi caso maintest2.c.
TI MSP430
P á g i n a | 56

Ahora regresamos al IAR y seleccionamos en la pestaña Overview, Add, Add


Files… y selecciona el archivo que justo acabamos de copiar de main.c:
TI MSP430
P á g i n a | 57

Ahora colócate en la pestaña test2 y da doble clic en maintest2.c, en la


pantalla derecha aparecerá otra pestaña con maintest2.c, el archivo que
justo acabamos de copiar.
TI MSP430

Esto es bastante útil cuando queremos hacer varias revisiones de un programa


o cuando simplemente vamos a agregar cosas menores. Es importante que se
seleccione la pestaña sobre la cual se quiere trabajar o de lo contrario no se
P á g i n a | 58

apreciaran cambios. Por ejemplo. Si nosotros estamos trabajando en


maintest2.c y tenemos seleccionada la pestaña test1 una vez que presionemos
Ctrl+D o seleccionemos Rebuild all el archivo que realmente se esté
compilando será main.c y será el mismo que sea programado al
microcontrolador. Es lo mismo en el caso de seleccionar la pestaña test2 y
estar programando en main.c... se compilara y programara maintest2.c en el
microcontrolador. Recuerda siempre presionar para guardar los cambios.

NOTA: Se tienen que hacer los mismos pasos para seleccionar el


microcontrolador (test2 – Deb… Options… Device… msp430x21x2) y el
programador (test2 – Deb… Options… Driver… FET Debugger) o de lo contrario
el compilador no programara nada.

Ahora sí, la práctica, borra el código que se encuentra en maintest2.c y pega


este:

//Programa para encender y apagar continuamente un LED por el puerto 1, pin 1


//(P1.1), me permití limpiar todos los comentarios del anterior programa
//Puesto que ya fueron explicados algunos comandos.
#include "msp430x21x2.h"

int main( void )


{
// Stop watchdog timer to prevent time out reset <- esto siempre lo agregan.
WDTCTL = WDTPW + WDTHOLD;
P1DIR=0xFF;
while(1) //Agregamos ciclo infinito.
{
P1OUT=0x01;
P1OUT=0x00; //Apagamos el LED.
}
return 0;
}

Terminado esto presiona Ctrl+D y el código será programado al


microcontrolador aparte de poner a IAR en modo debugger; agrega la
ventana de Registers si lo deseas (Selecciona Port 1/2) y oprime cuantas veces
quieras . Ya sea en la ventana de Registers o en el mismo micro podrás
observar cómo se prende y apaga el mismo LED, el programa nunca se saldrá
de modo debug por tener un ciclo infinito y solo estará oscilando en las
instrucciones P1OUT=0x01 y P1OUT=0x00 como se puede ver en las capturas.
TI MSP430
P á g i n a | 59

Ahora presiona el botón y observa lo que sucede… pareciera que el LED


siempre está encendido pero no es así, en realidad está prendiendo y
apagando con una frecuencia de según un osciloscopio Fluke de 117.5 KHz, lo
suficiente para no poder apreciarse el cambio. El botón lo que hace es
ejecutar el código continuamente (le deja al microcontrolador funcionar a su
velocidad estándar y por ello tampoco reporta datos a la computadora) En la
realidad es como nuestros programas serán ejecutados. La ejecución libre
puede ser parada con el botón .
TI MSP430

¿Qué hacer para apreciar el cambio? La función Delay mencionada más


arriba nos puede ayudar en esto. En la misma pantalla en que estamos, copia
el siguiente código justo después de #Include “msp430x21x2.h”
P á g i n a | 60

unsigned int delay (unsigned int x)


{
unsigned int i,j;
for (i = 0; i<= x; i++)
{
for(j=0;j<=1000; j++);
}
return 0;
}

Y entre cada P1OUT coloca un delay(1000); de tal manera que se vea así:

Hecho esto, presiona Alt+P+R, la pantalla parpadeara, mostrara algunos


mensajes, volverá a compilar y a programar el micro y luego te regresara al
modo debugger rápidamente, entonces ahora sí, presiona y observa el
resultado… ahora prenderá y apagará el LED muy lentamente. Ahí mismo sin
presionar nada, cambia el valor de delay a 50 y presiona nuevamente Alt+P+R
seguido de … la diferencia debe ser más apreciable… según mi
TI MSP430

osciloscopio 2.3 KHz. La función delay agrega un conteo el cual el


microcontrolador hace internamente, consumiendo recursos y por ende
retrasando el encendido y apagado del LED.
P á g i n a | 61

Ahora, hay una manera de ver más a fondo que está haciendo el programa
en sí. Una herramienta muy útil y poderosa del IAR es que se puede apreciar
que están haciendo las variables, notaras que hay un unsigned int i,j; para
observar su comportamiento podemos usar la ventana Locals.

Para el programa con y después selecciona View… Locals.

Entonces, dependiendo en qué momento lo paraste, los valores de la ventana


Locals pueden cambiar a los que se muestran aquí:

Para borrar esos valores presiona este botón resetea por completo tanto el
programa como la ejecución en el micro, colocándolo todo a cero. Realizado
esto, selecciona Debug en el menú principal y después Autostep…
TI MSP430
P á g i n a | 62

Selecciona Step Into (Source Level) y deja el valor de 1000 (es el tiempo en
que quieres que automáticamente cambie de instrucción, está en
milisegundos, si quieres que sea más rápido el cambio baja el valor, o si lo
prefieres más lento sube el valor)
TI MSP430

Presiona Start y observa la ventana de Locals… te muestra las variables y su


comportamiento a través del programa. Los valores en rojo indican que
variable ha cambiado su valor como se puede apreciar en las capturas.
P á g i n a | 63

Esperar a que termine seria eterno así puesto que j tiene que llegar a 1000, y
cada vez que lo hace i es incrementado en 1 e i tiene que llegar a 50. Para el
ciclo con y termina el modo debug con , finalmente guarda todo
con .

 La practica 1 y 2 ya realizadas están aquí.

Con esto se termina la explicación del entorno IAR.


TI MSP430
P á g i n a | 64

Programación de microcontroladores MSP430:


Hasta el momento hemos concentrado nuestro estudio en básicos del
lenguaje C así como el uso del compilador IAR. Es momento entonces de ver
funciones más avanzadas de los microcontroladores de la serie MSP430.

En mi experiencia personal, me ha sido de gran utilidad mirar los ejemplos del


microcontrolador que estoy usando para después observar la hoja de datos
(Requisito casi indispensable, al lado del compilador siempre está la hoja de
datos del microcontrolador a usar) y verificar cual es la función de los registros.

Particularmente en IAR, me he dado cuenta que los registros siempre están a


la izquierda de las igualdades (por ejemplo P1DIR|=0x01; esta instrucción
indica que se le está sumando un 1 a lo que ya tenía el registro P1DIR) y que
estos registros poseen exactamente el mismo nombre que en la hoja de datos.

En el caso de cuando son registros que solo cambian de número, por ejemplo
P1DIR, P2DIR, P3DIR… etc. Estos son englobados en uno solo en la hoja de
datos como PXDIR, aquí una muestra:

18
TI MSP430

La hoja de datos no es cualquiera, es la Users Guide del microcontrolador en


particular, en la página de Texas Instruments se pueden encontrar tanto la del

18
No quería decirlo pero… el inglés es realmente importante aquí.
P á g i n a | 65

microcontrolador en sí como la Users Guide, que contiene todos los registros


habidos y por haber del microcontrolador en cuestión 19.

Sin más que decir, enfocaré las siguientes líneas a explicar programas
realizados en el IAR.

Manejo de puertos en MSP430.

Practica 3: Uso de entradas y salidas en los puertos.


El propósito de esta práctica es de manejar un solo puerto como entrada y
salida, haciendo uso de los últimos 4 pines del puerto 1 para 4 entradas y los
primeros 4 pines para salidas (LEDs).

Iniciaremos un nuevo proyecto en el IAR desde cero y copiaremos el siguiente


código al main.c:

//Programa para uso de entradas y salidas en un mismo puerto (P1)


//El funcionamiento de este programa es el de tomar los ultimos 4 bits del puerto 1
//(P1.4 - P1.7), guardarlos en una variable y posteriormente desplegarlos con LEDs
//en los primeros 4 bits de P1 (P1.0 -P1.3)
#include "msp430x21x2.h"

int main( void )


{
unsigned char x; //Variable para guardar la entrada, 8 bits.

WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer to prevent time out reset
P1DIR=0x0F; //Declaramos el puerto como 0000 1111 (Los ceros son la parte mas
//alta (P1.4-P1.7), y como ceros son entradas, los 1's son la
//parte baja (P1.0-P1.3) y como 1's son salidas.

P1OUT=0x00; //Solo por buena costumbre, iniciamos en ceros el registro del


//Puero 1.

while(1) //Ciclo Infinito para que este constantemente capturando y desplegando


//los valores del puerto 1.
{
x=P1IN; //Capturamos el valor de la parte alta (P1.4 - P1.7) en la variable x
P1OUT=(x>>4); //Y posteriormente desplegamos el valor corrido 4 bits a la derecha.
}
return 0;
}

Si todo sale bien, el microcontrolador, y los LEDs deberían hacer algo como
esto:
TI MSP430

19
Dejo entonces la hoja de datos “Users Guide” aquí para microcontrolador MSP430F2132:
PDFs\MSP430x2xxx_GRL.pdf
P á g i n a | 66

1 2

3 4

¿Qué es lo que realmente hace el programa?

Supongamos que en los dip-switch tenemos un 1010.

El registro P1IN entonces tendrá un valor de 1010 0000. Ese valor lo guardamos
en x con la instrucción:
TI MSP430

Pero después, con la instrucción:


P á g i n a | 67

Le ordenamos que saque por el puerto 1 el valor de x corrido 4 bits a la


derecha, en otras palabras, si x=1010 0000:

Entonces x>>4=0000 1010, dejando exactamente la parte alta exactamente


en la parte baja (y agregando ceros en la parte alta), y como declaramos que
solo los primeros 4 bits del puerto 1 (P1.0 – P1.3) serían de salida, el valor que
tomara este es únicamente 1010, tal cual como en la imagen 3 más arriba.

Esto lo hace continuamente gracias al ciclo infinito while(1), no está de más


decir que los puertos es necesario configurarlos una sola vez.

Se puede observar el programa a detalle usando las ventanas de Locals y


Register para ver tanto la variable x como los registros P1IN, P1OUT.

Fin de la práctica, si desean ver el proyecto hecho este está aquí.

Práctica 4: Uso de algunos ciclos finitos y condicionales + pines


individuales de entrada.
El objetivo de esta práctica será el de tomar dos entradas del puerto 1 (P1.4,
P1.5) y con ellas cambiar la velocidad de un conteo que será desplegado en
los primeros 4 bits del puerto 1 (P1.0-P1.3).

Nuevamente crea un nuevo proyecto y copia el siguiente código:

#include "msp430x21x2.h"

unsigned int delay (unsigned int x) //Esta funcion la hemos usado con anterioridad
{ //es la responsable de generar retardos para
unsigned int i,j; //nuestro programa.
for (i = 0; i<= x; i++)
{
for(j=0;j<=1000; j++);
}
return 0;
}

int main( void )


{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
P1DIR=0x0F; //declaramos de P1.7 hasta P1.4 como pines de entrada
//y a su vez P1.3 hasta P1.0 como pines de salida
while(1)
{
TI MSP430

unsigned char i; //Variable para nuestro ciclo FOR


for(i=0;i<=15;i++) //Este ciclo comienza el conteo en 0, continua contando
//contando hasta que i sea menor igual a 15 y su incremento
//es de 1 en 1. (15 en decimal, 1111 en binario)
{
P1OUT=i; //A la salida colocamos el conteo.
P á g i n a | 68

if((P1IN&0x10)==0x10){delay(1);} //Si presionamos P1.4 el valor para el retardo es de 1


if((P1IN&0x20)==0x20){delay(15);} //Si presionamos P1.5 el valor para el retardo es de 15
if((P1IN&0x30)==0x30){delay(30);} //Si presionamos P1.4 y P1.5 valor para retardo=30.
//Si no presionamos nada no hay retardo.
}
}

return 0;
}

Si todo salió sin contratiempos los conteos deberían verse así:

Video sin delay.


Video con delay(1).
Video con delay(15).
Video con delay(30).

Para aquellos que vienen de Microchip… seguramente esperaban algo así


como un #bit pin1 0x05.1… desafortunadamente en los MSP430 la manera de
usarlos es diferente y más bien parece un arreglo.

Veamos la siguiente imagen:

El motivo de existir para las operaciones del tipo P1IN&0x10==0x10 es de


precisamente obtener el valor del puerto 1, filtrar el valor que nos interesa y
compararlo para dar la condición. Este es afectado por dos cosas: nuestros
dip-switch y aparte las salidas del mismo micro. Digamos que tenemos el dip-
TI MSP430

switch número 4 (con base en la imagen anterior) encendido y que el último


LED está prendido, esto en el registro P1IN se vería así:
P á g i n a | 69

0 0 0 1 0 0 0 1

Switch Switch Switch Switch LED 1 LED 2 LED 3 LED 4


1 (0x80) 2 (0x40) 3 (0x20) 4 (0x10) (0x08) (0x04) (0x02) (0x01)

Esto es un 0x11 en hexadecimal. Este valor después de la operación AND


(P1IN&0x10) quedaría así:

0 0 0 1 0 0 0 0

Switch Switch Switch Switch LED 1 LED 2 LED 3 LED 4


1 2 3 4 (0x10)

Obteniendo el valor de 0x01 en hexadecimal, cuando finalmente es


comparado con la igualdad (0x01==0x01) el IF se activa, dando paso a la
instrucción delay(1); e ignorando las demás (a menos que después se suscite
un cambio en los dip-switch).

La forma en que se ignoran los demás es sencilla, al haber 3 IF la operación se


repite tres veces, obteniendo el mismo resultado de 0x01, este valor es
comparado con los 3 IF, en el primero es 0x01==0x01 el cual dará verdadero,
en el segundo es 0x01==0x02 el cual evidentemente dará falso y el tercero
0x01==0x03 también dará falso.

En el caso de cuando se presionan dos dip-switch y el mismo LED encendido:

0 0 1 1 0 0 0 1

Switch Switch Switch Switch LED 1 LED 2 LED 3 LED 4


1 (0x80) 2 (0x40) 3 (0x20) 4 (0x10) (0x08) (0x04) (0x02) (0x01)

Lo cual es un 0x31 en hexadecimal, entonces después de la operación AND


(P1IN&0x30):

0 0 1 1 0 0 0 0

Switch Switch Switch Switch LED 1 LED 2 LED 3 LED 4


1 2 3 (0x20) 4 (0x10)

Que ahora es un 0x30 en hexadecimal, que a su vez hará que la tercer


condición se active (0x30==0x30) llamando a la instrucción delay(30);

Fin de la práctica, la misma la tienes aquí.

Practica 5: uso de pines individuales de salida.


TI MSP430

Para cerrar con broche de oro el uso de los puertos, tanto de entrada como
de salida se mostrará el uso individual de los pines, en esta ocasión para ser
usados como salidas individuales.
P á g i n a | 70

Observemos el siguiente código (De preferencia ejecutar en el IAR):

#include "msp430x21x2.h"

int main( void )


{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
P1DIR=0x0F; //Asignamos medio puerto de entrada y medio de salida
while(1)
{
if((P1IN&0x10)==0x10){P1OUT|=0x01;} //Si presionamos el switch en P1.4 entonces
//suma un 1 (P1.0) al puerto 1
else {P1OUT&=~0x01;} //De lo contrario, multiplica por la negacion de
//0000 0001
if((P1IN&0x20)==0x20){P1OUT|=0x02;} //Si presionamos el switch en P1.5 entonces
//suma un 2 (P1.1) al puerto 1
else {P1OUT&=~0x02;} //De lo contrario, multiplica por la negacion de
//0000 0010
if((P1IN&0x30)==0x30){P1OUT|=0x03;} //Si presionamos el switch en P1.4 y P1.5 entonces
//suma un 3 (P1.0 y P1.1) al puerto 1
else {P1OUT&=~0x03;} //De lo contrario, multiplica por la negacion de
//0000 0011
}
return 0;
}

Este programa es aplicación directa de lo que hicimos anteriormente con las


entradas individuales, se debe apreciar algo más o menos como esto:

1 2
TI MSP430
P á g i n a | 71

3 4

¿Cuál es la diferencia respecto al primer programa?

Que en está ocasión se controló a voluntad el encendido o apagado de los


pines. Mientras que el primer programa lo único que hacía era depender
directamente del estado de los dip-switch, este puede apagarlos ó prenderlos
cuando se requiera.

Explicando el programa:

La parte de los IF se vio anteriormente, aquí lo usamos solo para poder tener
varias entradas individuales.

Si alguno de los IF se cumple entonces una instrucción parecida a


P1OUT|=0x… será ejecutada.

Por ejemplo, si nosotros encendemos el primer dip-switch la instrucción a ser


llamada será P1OUT|=0x01;

El objetivo de esta instrucción es sumar (|=) un 1 binario al puerto 1. De esta


manera aseguramos dos cosas:

 Que encienda el LED.

 No molestar a los otros bits, puesto que es una suma, el resultado en el


registro P1OUT no es más que el valor que ya tenía más 1.

Como esta operación es lógica y no aritmética, el resultado de sumar 1+1


siempre será 1 (Esta es la razón por se suman constantemente los bits, como si
fuera el contador del programa pasado).

P1OUT|=0x02; y P1OUT|=0x03; hacen lo mismo con el puerto 1. Recuerden que


TI MSP430

0x01=0000 0001, 0x02=0000 0010 y 0x03=0000 0011.

En el programa se pueden apreciar también varios else, esto nos sirve para
que cuando el dip-switch no sea presionado automáticamente tome la
P á g i n a | 72

acción else de un IF (Como decir, si esto no se cumple, de cualquier otra


manera esto…)

Siguiendo con el mismo dip-switch del P1.4, cuando este no se encuentra


encendido la acción a tomar es P1OUT&=~0x01;

Supongamos que hemos presionado los dos dip-switch, en el registro P1OUT


tenemos un 0000 0011. Pero entonces apagamos el primer dip-switch, las
operaciones que realiza el else son estas:

~0x01

Aquí realiza un NOT de un byte entero, en este caso 0x01=0000 0001, entonces
~0x01=1111 1110.

P1OUT&=~0x01

Aquí una vez realizada la operación del NOT la operación pasaría a ser
P1OUT&=1111 1110 y esta a su vez pasaría a ser 0000 0011&=1111 1110.

Entonces:

Las demás operaciones se explican por sí solas, 0x02=0000 0010, 0x03=0000


0011, entonces ~0x02=1111 1101 y ~0x03=1111 1100.

Entonces nosotros podemos hacer uso de las instrucciones P1OUT|= 0x… y


TI MSP430

P1OUT&=~0x… cuando nosotros lo deseemos, puede ser terminando un ciclo o


realizando alguna operación.
P á g i n a | 73

Todas estas instrucciones aplican a todos los puertos, basta con cambiar el 1
por el puerto deseado a utilizar, puede ser P2OUT, P3OUT… etc.

Fin de la práctica, la misma, ya hecha, la puedes ver aquí.

Manejo de periféricos en MSP430.

ADC (Analogic to Digital Converter)

Practica 6: Uso del ADC.


Hasta el momento habíamos limitado nuestro estudio al manejo de pines en el
microcontrolador, ya sea de entrada o de salida, sin embargo algunos de
estos pines poseen funciones adicionales.

Si observamos cautelosamente la imagen de configuración de pines para el


micro20 que estamos usando, se puede observar que más de un pin tiene una
función.

Aquellos pines con una etiqueta A1, A2… etc. son entradas analógicas; estas
pueden recibir voltajes no solo digitales, si no en el rango completo de V- a V+
volts (Casi siempre se manejan de 0 a 3v).

El siguiente programa hace uso de la siguiente configuración en el micro para


funcionar:
TI MSP430

20
Aquí les dejo el PDF del MSP430F2132, la configuración de pines se encuentra ahí.
P á g i n a | 74

Y aquí el programa, se puede usar en un proyecto creado con el IAR 21:

//Programa para usar el ADC 0 del microcontrolador MSP430F2132.


//El micro requiere de una referencia positiva conectada al pin P2.4.
#include "msp430x21x2.h"

int main( void )


{
unsigned char d;
unsigned int resultado;

// Stop watchdog timer to prevent time out reset


WDTCTL = WDTPW + WDTHOLD;

ADC10CTL1 = INCH_0 + ADC10DIV_0;


// ADC0 + clk dividido entre 0 para este clk (sin prescaler)

ADC10CTL0 = SREF_2 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;


// referencia Veref+ para Vref+ y VSS para Vref- (un pin sin cableado) +
// Ciclos de reloj para 'sample and hold' provenientes del ADC10CLK +
// Generador de referencia encendido + Habilitar ADC + Habilitar Interrupciones del ADC +
// Reduce la frecuencia de muestreo para reducir consumo de amperaje.

for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilize.

P1DIR=0x0F; //Para prender y apagar foquitos.

while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)

__bis_SR_register(CPUOFF + GIE);
TI MSP430

// LPM0 (Low Power Mode 0) Modo de ahorro de energia +


//GIE (General Interrupt Enable) Habilitacion de interrupciones

21
Y me acordaba del… read_adc();
P á g i n a | 75

resultado = ADC10MEM; // De aqui obtenermos el resultado del ADC.


P1OUT=resultado/64; //Dividimos y deplegamos el resultado con los LEDs
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}

return 0;
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


//la conversión.
__interrupt void ADC10_ISR(void)
{
__bic_SR_register_on_exit(CPUOFF); // Clear CPUOFF bit from 0(SR) Sale
// del modo de bajo consume de energia.
}

Imágenes y video del programa en funcionamiento:

1 2

3 4
TI MSP430
P á g i n a | 76

5 6

Video en funcionamiento.

Básicamente el microcontrolador toma una lectura, la divide entre 64 (El valor


de la lectura máximo es de 1023, entre 64 nos da un número máximo de 16
que son los 4 bits, correspondientes a los LEDs para poder desplegarlo) y la
despliega continuamente en los LEDs.

Como se puede apreciar, el manejo del ADC es mucho más complejo que en
un micro de la marca Microchip; esto debido principalmente a que se requiere
del uso de registros para poder operarlo.

Trataré de explicar los registros conforme se van presentando en el programa:

La primera línea que contiene uno es esta:

Lo primero que hay que observar es que a la izquierda siempre se encuentran


los registros y a la derecha se encuentran los bits de configuración.

Los nombres de estos registros no vienen a ser un misterio, se encuentran


directamente en la hoja de datos del microcontrolador 19:
TI MSP430
P á g i n a | 77

Mucho menos los bits, que vienen a ser explicados justo después del registro:

Lo que significa, que con la hoja de datos nos podemos dar idea de la
TI MSP430

operación de los registros, y no solo eso ya podemos controlarlos a voluntad


(En la hoja de datos basta con poner a buscar el registro tal cual está en el
programa para encontrarlo).
P á g i n a | 78

Lo primero que haremos será configurar el registro ADC10CTL1. Conforme al


orden del programa:

INCH_0: este bit de configuración toma su nombre de IN CHANNEL, que como


su nombre lo indica nos permite seleccionar cual ADC queremos usar:

A0 viene a ser INCH_0, A1 INCH_1… etc. el microcontrolador también posee un


sensor de temperatura integrado (posteriormente se verá su uso), este también
opera como un ADC con la diferencia que este no necesita conexión externa.

El bit de configuración ADC10DIV_0 se comporta como un preescaler;


básicamente un prescaler es un divisor de frecuencia, en este caso:

ADC10DIV_0 viene a ser una división entre 1, es decir, el reloj que se escoja
posteriormente para su funcionamiento no será dividido entre nada y
funcionara a la velocidad del mismo, ADC10DIV_1 configura una división entre
2, si nosotros tenemos un reloj de 8 MHz, la frecuencia a la cual funcionará el
TI MSP430

ADC será a 4 MHz… y así sucesivamente. Sin complicarnos mucho,


ADC10DIV_0 es una opción viable para la mayoría de las aplicaciones.
P á g i n a | 79

La siguiente línea de código es un poco más extensa en cuanto a


configuración del registro:

Este registro en particular define parámetros externos al micro (referencias por


ejemplo) y además controla el encendido y apagado del mismo.
Continuando con bits de configuración:

SREF_2 nos indica cómo es que queremos las referencias. En todo buen ADC se
especifican, mediante otros pines ajenos a los de medición, los rangos
máximos y mínimos del mismo.

Si nosotros tenemos un valor que fluctúa entre los 0 y los 5 volts la referencia
negativa vendría a ser un valor constante de 0 volts y la referencia positiva un
valor constante de 5 volts, si es un valor que fluctúa de -3 volts a 3 volts… las
referencia negativa y positiva serían -3 volts y 3 volts constantes
respectivamente. Las referencias son usadas para que el ADC conozca de
donde iniciar con un 0 en binario, y para este caso al ser un ADC de 10 bits,
donde termina con un 1023 binario.

Para el ejemplo que usé tenemos un voltaje de 0 a 3 volts, como es de 0… es


decir tierra o GND, no necesitamos una referencia externa y podemos usar la
del mismo microcontrolador. En caso de los 3 volts forzosamente la referencia
tiene que ser externa, el microcontrolador ofrece una referencia positiva de
TI MSP430

hasta 1.5 volts (si nosotros intentamos medir algo más arriba de eso en el
resultado del ADC siempre obtendremos 1023, que es el número máximo de los
10 bits de conversión)
P á g i n a | 80

En la imagen de arriba se pueden apreciar los pines específicos para las


referencias externas, pero estas pueden ser usadas o no dependiendo del bit
de configuración, Nosotros usamos SREF_2 que viene a ser referencia negativa
interna (proporcionada por el microcontrolador), y referencia positiva externa
proporcionada por nosotros como en la imagen con el potenciómetro más
arriba.

Todo esto se encuentra en la hoja de datos User’s Guide que mencione


anteriormente.

000 vendría a ser SREF_0, referencia positiva = VR+ = Vcc (3v Interna) y
referencia negativa =VR- = Vss (0v Interna) es decir, la misma alimentación del
microcontrolador.

001 es SREF_1 y aquí VR+ = Vref+ (1.5v interna) y VR- = Vss (0v interna)
TI MSP430

002 = SREF_2. Esta configuración es una de las más usadas, si no se desea tener
un límite de hasta 3 volts, si no algo un poco mayor como por ejemplo 5 volts
se requiere forzosamente de una referencia positiva externa. Resumiendo, esta
configuración VR+ = Veref+ = Referencia externa en el pin P2.4 (5 volts
P á g i n a | 81

constantes para el caso que mencioné anteriormente) y VR- = Vss (0v


referencia interna)

De acuerdo a las necesidades se puede seleccionar a gusto el SREF que mejor


se acomode a la situación.

ADC10SHT_3: probablemente muchos no estén familiarizados con un término


llamado sample and hold22, escuetamente hablando para este micro son
ciclos de reloj que usa para retener una muestra (y ser capaz de medirla),
usado sobre todo cuando los valores están cambiando mucho. Para no hacer
más larga la explicación del ADC, ADC10SHT_3 retiene 64 ciclos de reloj la
muestra, si se requieren menos puede ir bajando el ultimo numero. Consultar
principalmente en la hoja de datos.

REFON inicializa el generador de referencia interno del micro, sirve para poder
usar las referencias internas.

ADC10ON enciende el modulo ADC del microcontrolador, al ser los MSP430


muy estrictos con el manejo de energía, muchos de los módulos vienen
apagados por default.

ADC10IE habilita las interrupciones del modulo ADC, son muy usadas por
ejemplo para apagar el procesador mientras se está realizando la conversión
analógico digital.

ADC10SR Reduce el consumo de energía aun más al reducir de 200 kilo


muestras por segundo (en inglés kilo samples per second o ksps) a 50, si se
desea un funcionamiento más rápido basta con no colocar este bit de
configuración.

Después de un retardo pasamos a la siguiente línea de instrucción (Ya adentro


de un ciclo infinito while):

Aquí, puesto que ya habíamos configurado con anterioridad el registro


ADC10CTL0 lo que se hace para comenzar la conversión es sumar dos bits más
de configuración:

ENC habilita la conversión y ADC10SC comienza la conversión, es aquí cuando


nuestro ADC comienza a convertir de un voltaje analógico a uno digital. En
TI MSP430

este punto nuestro ADC se encuentra ya funcionando.

22
Aquí explican más a detalle este concepto http://en.wikipedia.org/wiki/Sample_and_hold
P á g i n a | 82

Después nos adentramos en esta instrucción:

CPUOFF se refiere tal cual a apagar el CPU del microcontrolador, esto para
entrar en modo de bajo consumo de energía y además GIE se encarga de
habilitar las interrupciones, en este caso, la responsable de que el CPU vuelva
a despertar y continúe haciendo operaciones:

La parte roja de la parte de arriba declara la interrupción del ADC cuando


este termina la conversión.

Una interrupción es una petición que se le hace a algún procesador, y al ser


interrupción el procesador interrumpe lo que está haciendo para atender esta
petición. Como habilitamos el modo de bajo consumo de energía pero
dejamos habilitadas las interrupciones el procesador hará caso a la misma.

Se puede apreciar que hay un void ADC10_ISR(void), esta es una función tal
cual que se coloca para especificar qué proceso ha de realizar el
microcontrolador una vez recibida la interrupción.

Nuestra única línea de código que hay en esta función es la de despertar al


procesador de su estado apagado para que nos permita continuar
exactamente en donde nos quedamos del programa.

Una vez que la interrupción es completada el programa continua


normalmente donde se quedó:

Finalmente, para leer el valor proveniente de la conversión ADC solo basta con
volcar el contenido del registro de 10 bits ADC10MEM en una variable
TI MSP430

cualquiera, para este caso se declaró una variable resultado:


P á g i n a | 83

Y una vez volcada en el resultado la desplegamos en 4 LEDs para observar el


incremento o decremento del POT colocado anteriormente. Como son solo 4
bits tenemos hasta 16 combinaciones posibles, y como 16x64=1024 con ellas
abarcamos todo el rango de 0 a 1024 (por ello la división entre 64).

Finalmente, para detener la conversión deshabilitamos el bit ENC que


habilitamos anteriormente, de esta manera al estar dentro del ciclo while, la
conversión se puede habilitar nuevamente y generar otra conversión en poco
tiempo.

NOTA: El hecho de mostrar los registros y cada uno de ellos es de demostrar su


uso correcto mediante la hoja de datos User’s Guide19. La principal técnica
para entender cualquier programa hecho para MSP430 es mirar la hoja de
datos y observar los registros con sus respectivos bits de configuración. Para
aquellos que vienen de Microchip esto puede ser al principio algo
complicado, pero la forma en que lo hacen los MSP430 es una forma más
aproximada a como se usan otra clase de microcontroladores… haciendo
principal uso de los registros para auxiliarse en su configuración.

Conclusión: siempre que se quiera programar un MSP430 se debe tener a la


mano la hoja de datos del chip en particular a programar y también su User’s
Guide.

Los siguientes programas solo se centrarán en algunos bits de configuración


especiales para un funcionamiento correcto, el resto dependerá del usuario al
consultar sus respectivos registros en las hojas de datos correspondientes23.

Casi lo olvidaba, la práctica la tienes aquí.

Practica 7: Dos ADCs.


Este programa hace uso de más ADCs simultáneamente, pudiendo
seleccionar cada uno individualmente mediante los dip-switch. La
configuración correspondiente al circuito es esta:
TI MSP430

23
El uso del inglés es… desafortunadamente fundamental para el entendimiento de los mismos. Con lo
mucho que no me gusta decirlo.
P á g i n a | 84

Y aquí el programa:

//Programa para usar el ADC 0 y 1 del microcontrolador MSP430F2132.


//El micro requiere de una referencia positiva conectada al pin P2.4.
//Para usar el canal 0 el dip switch 1 tiene que estar activado
//Para usar el canal 1 el dip switch 2 tiene que estar activado
//si se activan dos dip switch la salida en los LEDs sera 0
#include "msp430x21x2.h"

int main( void )


{
unsigned char d;
unsigned int resultado;

// Stop watchdog timer to prevent time out reset


WDTCTL = WDTPW + WDTHOLD;

ADC10CTL1 = ADC10DIV_0;
// ADC0 + clk dividido entre 0 para este clk (sin prescaler)

ADC10CTL0 = SREF_2 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;


// referencia Veref+ para Vref+ y VSS para Vref- (un pin sin cableado) +
// Ciclos de reloj para 'sample and hold' provenientes del ADC10CLK +
// Generador de referencia encendido + Habilitar ADC + Habilitar Interrupciones del ADC +
// Redice la frecuencia de muestreo para reducir consumo de amperaje.

for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilice.

P1DIR=0x0F; //Para prender y apagar foquitos.

while(1)
{

if((P1IN&0xF0)==0x10) //Si el dip switch 1 es presionado


{
ADC10CTL1 &= ~INCH_1; //Deshabilita canal 1
TI MSP430

ADC10CTL1 |= INCH_0; //Y activa el canal 0


}
else if((P1IN&0xF0)==0x20) //Si el dip switch 2 es presionado
{
ADC10CTL1 &= ~INCH_0; //Deshabilita canal 0
ADC10CTL1 |= INCH_1; //Y activa el canal 1
P á g i n a | 85

}
if(!(((P1IN&0xF0)==0x00)|((P1IN&0xF0)==0x30)))
//Si ambos dip-switch NO estan
//apagados o prendidos realiza
//la conversion ADC.
{
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)

__bis_SR_register(CPUOFF + GIE);
// LPM0 (Low Power Mode 0) Modo de ahorro de energia +
//GIE (General Interrupt Enable) Habilitacion de interrupciones
resultado = ADC10MEM; // De aqui obtenermos el resultado del ADC.

P1OUT=resultado/64;
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}
else
{
P1OUT=P1OUT&0xF0; //Si de lo contrario
//estan prendidos o apagados los dos
} //Manda un cero a la salida.
}

return 0;
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


//la conversión.
__interrupt void ADC10_ISR(void)
{
__bic_SR_register_on_exit(CPUOFF); // Clear CPUOFF bit from 0(SR) Sale
// del modo de bajo consume de energia.
}

Video del programa en funcionamiento.

Solo comentaré un par de líneas que creo necesario hacerlo:


TI MSP430

Este es un if con varias condiciones en su argumento, para no perderse en el


mismo recomiendo seguir las instrucciones conforme a los paréntesis, de
interiores a exteriores.
P á g i n a | 86

El orden iría así:

(P1IN&0xF0)==0x00

En paralelo con:

(P1IN&0xF0)==0x30

Ambas instrucciones se utilizan para detectar si los dip-switch están los dos
encendidos o los dos apagados.

Para tomar ambas instrucciones en cuenta usamos |

Es por eso el siguiente paréntesis:

!((P1IN&0xF0)==0x00)|((P1IN&0xF0)==0x30))

Se ha de recordar que | corresponde a una operación OR, si algunas de las


condiciones se cumplen el resultado será 1, no importando el orden.

Finalmente el ! es un NOT que en el caso de que alguna de las dos condiciones


anteriores (!((P1IN&0xF0)==0x00)|((P1IN&0xF0)==0x30))) corresponda a un 1
este operando lo hará 0 y viceversa. De esta manera, si los dos dip-switch
están encendidos o apagados siempre se tendrá un cero al final del NOT.

Si el resultado es 1, el if dejará pasar su condición, que es la de comenzar a


usar el ADC y obtener un resultado del mismo, de lo contrario, si es un 0 solo
dejara pasar la condición dentro del else que es un cero a la salida de los
LEDs.

La práctica entonces, la tienes aquí.

Practica 8: El sensor de temperatura interno.


Es exactamente lo mismo que manejar los ADCs, salvo que en esta ocasión no
se requiere de cableado externo (SREF cambia):

//Programa para usar el sensor de temperatura del microcontrolador MSP430F2132.

#include "msp430x21x2.h"

int main( void )


{
unsigned char d;
volatile long res; //Asegurarse de que sean LONG las variables a utilizar
//para la temperatura, de lo contrario el programa no funciona
volatile long res2, res3; //res3 solo es para desplegar el dato en Watch o Locals
TI MSP430

//por alguna razon, si no se agrega volatile


//no me deja ver en Locals o Watch el resultado
//de res2 que se encuentra en grados centigrados

// Stop watchdog timer to prevent time out reset


WDTCTL = WDTPW + WDTHOLD;
P á g i n a | 87

ADC10CTL1 = INCH_10 + ADC10DIV_0;


// ADC0 + clk dividido entre 0 para este clk (sin prescaler)

ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;


// referencia 1.5v (interna) para Vref+ y VSS para Vref- (sin cableado) +
// Ciclos de reloj para 'sample and hold' provenientes del ADC10CLK +
// Generador de referencia encendido + Habilitar ADC + Habilitar Interrupciones del ADC +
// Redice la frecuencia de muestreo para reducir consumo de amperaje.

for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilice.

P1DIR=0x0F; //Para prender y apagar foquitos.

while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)

__bis_SR_register(CPUOFF + GIE);

// LPM0 (Low Power Mode 0) Modo de ahorro de energia +


//GIE (General Interrupt Enable) Habilitacion de interrupciones

res = ADC10MEM; // De aqui obtenermos el resultado del ADC.


P1OUT=res/64; // Resultado tal cual, desplegado en 4 LEDs.
res = ADC10MEM; // De aqui obtenermos el resultado del ADC.
res2 = res*423/1024 - 278; // Resultado en grados centígrados
res3 = res2; // variable que nos permite ver el resultado en grados c
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}

return 0;
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


//la conversión.
__interrupt void ADC10_ISR(void)
{
__bic_SR_register_on_exit(CPUOFF); // Clear CPUOFF bit from 0(SR) Sale
// del modo de bajo consume de energia.
}

Video de la práctica en funcionamiento.

Hay… quizás un dogma en este programa que me gustaría aclarar: el


resultado de la fórmula para transformar a grados centígrados viene de aquí:

oC = ((A10/1024)*1500mV)-986mV)*1/3.55mV = A10*423/1024 – 278

Cada grado tiene una resolución de 3.55 mV, y el ADC puede dar como
máximo un numero de 1024… (Por los 1000 se obtenía aproximadamente 135
grados), por si solita la medición de la temperatura contiene un offset, es por
TI MSP430

eso la resta de 986.

Ya lo mencioné en el programa, pero aun así me gustaría repetirlo, para las


operaciones de temperatura es preferible usar tipos de variable LONG, de lo
P á g i n a | 88

contrario el programa puede dar resultados inesperados. Para saber el valor


de la temperatura se puede usar en el menú principal View->Watch como lo
muestra la siguiente imagen:

Al principio no se desplegará nada, pero posicionando el cursor en res3 y


seleccionado el programa automáticamente hará un paro en esa
instrucción permitiéndonos desplegar el resultado de la variable res2:
TI MSP430
P á g i n a | 89

Entonces:
TI MSP430

Res2 marca que estamos a 28 grados.

Con esta práctica se concluye el uso de los ADCs. La misma la tienes aquí.
P á g i n a | 90

Timers (Temporizadores)

Práctica 9: PWM (Pulse With Modulation)


PWM se refiere a Modulación por Ancho de Pulso ó, lo que es lo mismo,
aumentar o disminuir un voltaje promedio en un tren de pulsos.

Quizás sea uno de los elementos más usados para controlar motores de DC,
iluminación de LEDs, flujo de algún fluido… etc.

El hecho de que esté en la sección de temporizadores, es porque con los


mismos se puede manejar el periodo y el duty cycle, ciclo de trabajo o tiempo
en alto, lo cual controla a su vez el voltaje promedio.

Pero… primero lo primero.

Hasta el momento hemos visto el uso de algunas funciones como puertos de


entrada y salida las cuales no requieren del algún registro extra, no así en el
caso del PWM.

El PWM, al ser una función inherente al uso de los timers requiere de la


configuración de un registro en particular, el PxSEL, donde la x es el número de
puerto.

Las funciones que activa dicho registro se encuentran localizadas en la hoja


de datos donde originalmente observamos la configuración de los pines para
el MSP430F213220. Por ejemplo, si nosotros quisiéramos seleccionar la función 2
del pin 24 (P1.3):
TI MSP430
P á g i n a | 91

La hoja de datos nos menciona que hay que poner un 1 en el bit


correspondiente. Por ejemplo si en P1OUT cada uno de los 8 bits corresponde a
un pin de salida (0x01 para P1.0, 0x02 para P1.1… etc.) entonces para P1SEL,
cada uno de los bits que puede almacenar en el registro representa la
segunda función de cada pin (0x01 para segunda función de P1.0, 0x02 para
segunda función de 0x02… etc.) de tal manera que, como queremos la
segunda función del P1.3 que es TA0.2, entonces para activarla requeriríamos
colocar una instrucción similar a P1SEL=0x08; (el pin P1.3 está representado
para P1OUT como 0x08). Si yo entonces quisiera activar la segunda función de
dos pines a la vez, supongamos P1.3 y P1.2 entonces la suma de ambos
requiere ser puesta en P1SEL, para P1OUT P1.3 solo representa un 0x08, para
P1.2 representa 0x04, entonces para prender los dos con P1OUT sería 0x08 más
0x04, lo cual nos da con la calculadora de Windows 0x0C. Finalmente la
instrucción es P1SEL=0x0C; la cual activa dos funciones segundas de los
respectivos pines mencionados anteriormente. Para los análogos en particular
no se requiere del uso de PxSEL.

Por cada puerto existe un PxSEL, para el puerto 1 P1SEL, para el puerto 2
P2SEL… etc. Además cuando los pines tienen más de dos funciones es posible
que se requiera el uso de PxSEL2 que se usa de la misma manera que PxSEL
(consultar en la hoja de datos previamente).

Explicado esto podemos entonces proceder a ver el programa (Sugiero


copiarlo al IAR para verlo en colores):

//Programa para usar 3 PWMs Independientes, la seleccion de los mismos se da con


//los dip-switch conectados a P1.4 y P1.5, el valor del duty-cycle puede ser
//cambiado con un potenciometro en A0 (P2.0) para cada uno de ellos.
//(Referencia positiva externa conectada a P2.4 de 3v)

#include "msp430x21x2.h"
volatile unsigned int res;
TI MSP430

void main(void)
{
unsigned char d;
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/***********************************************Inicio Configuracion ADC*/
P á g i n a | 92

ADC10CTL1 = INCH_0 + ADC10DIV_0;


ADC10CTL0 = SREF_2 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilize.
/***********************************************Fin configuracion ADC*/

P1DIR = 0x0F; // Dip-switch P1.7 - P 1.4, LEDs P1.3 - P1.0.


P1SEL|= 0x0C; // P1.2 and P1.3 TA0.1 y TA0.2 Habilitados
P3DIR = 0x80; // Salida PWM P3.7
P3SEL|= 0x80; // P3.7 TA1.1 Habilitado

TA0CCR0 = 1023; // PWM Period TA0


TA1CCR0 = 1023; // PWM Period TA1

TA0CCTL1 = OUTMOD_7; // TA0CCR1 reset/set


TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode

TA0CCTL2 = OUTMOD_7; // TA0CCR2 reset/set


TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode

TA1CCTL1 = OUTMOD_7; // TA1CCR1 reset/set


TA1CTL = TASSEL_2 + MC_1; // SMCLK, up mode

while(1)
{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
__bis_SR_register(GIE); // Habilitar interrupciones
/*******************************************Fin Captura valor ADC*/

if((P1IN&0xF0)==0x10)
{
if((P1OUT&0x03)!=0x01){P1OUT=0xFC; P1OUT|=0x01;}
TA0CCR1 = res; // TA0CCR1 PWM duty cycle
}
if((P1IN&0xF0)==0x20)
{
if((P1OUT&0x03)!=0x02){P1OUT=0xFC; P1OUT|=0x02;}
TA0CCR2 = res; // TA0CCR2 PWM duty cycle
}
if((P1IN&0xF0)==0x30)
{
if((P1OUT&0x03)!=0x02){P1OUT=0xFC; P1OUT|=0x03;}
TA1CCR1 = res; // TA1CCR1 PWM duty cycle
}
if((P1IN&0xF0)==0x00)
{
if((P1OUT&0x03)!=0x00){P1OUT=0xFC;}
TA0CCR1 = 0; // TA0CCR1 PWM duty cycle TA0.1
TA0CCR2 = 0; // TA0CCR2 PWM duty cycle TA0.2
TA1CCR1 = 0; // TA1CCR1 PWM duty cycle TA1.1
}
}
}
TI MSP430

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


__interrupt void ADC10_ISR(void)
{
res = ADC10MEM; // De aqui obtenermos el resultado del ADC.
ADC10CTL0 &= ~ENC; // Apagamos el ADC
P á g i n a | 93

Videos del programa funcionando: 1, 2.

Como se puede ver es una manera atractiva de controlar casi cualquier cosa
que funcione con DC.

Lo primero que hay que notar acerca del programa, es que todo aquello que
es configuración solo se necesita hacer una vez y por ende, puede estar fuera
del ciclo infinito while:

La configuración presenta varias cosas a remarcar, primero, las nomenclaturas


de los registros relacionadas con los pines:
TI MSP430
P á g i n a | 94

Una pista, los números dicen mucho. TA0CTL abarca a todos los TA0
(independiente mente si sean TA 0.0, TA 0.1… etc.) y de igual manera TA1CTL
abarca a todos los TA1.

Segundo, para ser breves, si se quiere un PWM simple sin control del periodo
basta con modificar TA0CCR0 (Para el TA 0.1 y TA 0.2) y TA1CCR0 (Para TA1.1)

El valor que se le inserta a estos registros, dígase TA0CCR0=1023; definirá el


ancho total del ciclo de trabajo (periodo) con un numero de 1023; esto de
manera que podamos controlar TA0CCR1 y TA0CCR2 en un valor de 0 a 1023
(duty cycle, lo mismo que nos da nuestro ADC de 10 Bits), esto aplica también
para TA1CCR0 y TA1CCR1.

Una manera, un poco más compleja pero más explicativa de cómo opera el
PWM en base a la configuración que le dimos se puede apreciar entiendo la
imagen en el PDF User’s Guide19 (en la página 406) del microcontrolador que
estamos usando:
TI MSP430
P á g i n a | 95

La única función de los if localizados en el código es tanto para diferenciar


que PWM se va a usar usando los dip-switch así como para prender los LEDs de
acuerdo a que dip-switch se va prendiendo.

Es importante notar, que a pesar de que no apagamos el CPU para mantener


un modo de bajo consumo, es entonces imprescindible mantener las
interrupciones mediante la instrucción:

Ya que de lo contrario la interrupción más abajo, cuya única instrucción es de


capturar el valor obtenido del ADC y apagar el ADC nunca se activará.

Nota: es posible que se aprecie un parpadeo cuando el potenciómetro esta


en cero volts, esto es por la configuración que estamos usando del ADC por el
momento.
TI MSP430

Fin de la práctica con PWM básico, la misma la tienes aquí.


P á g i n a | 96

Practica 10: PWM calculado, parte 1.


Muchas veces el PWM por si solo nos ayuda a controlar motores, LEDs… etc.
que funcionan con DC, sin embargo existen de algunas otras aplicaciones
como el manejo de servo motores que requieren de un PWM con un periodo
más o menos exacto de 2 milisegundos.

Para calcular el periodo la formula es algo simple. Suponiendo un periodo de 2


milisegundos = 0.002 segundos (frecuencia 1/T = 500Hz).

Entonces 0.002/(1/frecuencia_micro) = 0.002/(1/1100000) = 2200

En su estado normal, sin configurar nada como hemos estado haciendo todas
estas prácticas el microcontrolador tiene un reloj interno trabajando a 1.1Mhz,
es por eso la selección de esa velocidad en la ecuación de arriba.

Dicho esto, el programa es muy similar al de arriba, solo que en esta ocasión se
decidió usar solo un PWM:

//Programa para generar un PWM de aproximadamente 2 milisegundos de periodo

#include "msp430x21x2.h"
volatile unsigned int res;

int main( void )


{
unsigned char d;
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;

/***********************************************Inicio Configuracion ADC*/


ADC10CTL1 = INCH_0 + ADC10DIV_0;
ADC10CTL0 = SREF_2 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilize.
/***********************************************Fin configuracion ADC*/

P1DIR = 0x0F; // P1.2 and P1.3 output


P1SEL |= 0x04; // P1.2 and P1.3 TA1/2 otions

TA0CCR0 = 2200; // PWM Period

/*Calculo de la frecuencia y periodo de un PWM.


TA0CCR0 realmente es solo un contador que se mueve a la velocidad del
micro, así, sin configuraciones extras el micro corre a 1.1 MHz.
De tal manera que 2 miliseguindos = 0.002 segundos... entonces para el
calculo del periodo: 0.002/(1/1100000)=2200
Recordar que f=1/T y T=1/f.
*/

TA0CCTL1 = OUTMOD_7; // TA0CCR1 reset/set


TI MSP430

TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode


P1OUT&=0xF4;
__bis_SR_register(GIE); // Habilitar interrupciones

while(1)
P á g i n a | 97

{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
/*******************************************Fin Captura valor ADC*/
TA0CCR1 = res; // TA0CCR1 PWM duty cycle
}

return 0;
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


__interrupt void ADC10_ISR(void)
{
res = ADC10MEM; // De aqui obtenermos el resultado del ADC.
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}

Video del programa en funcionamiento.

En el video, podrás apreciar que la frecuencia es de 536 Hz, bastante


impreciso para nuestro cálculo, esto es debido a que el reloj interno de nuestro
microcontrolador posee una tolerancia muy grande. Se puede jugar un poco
decrementando el valor de TA0CCR0 hasta que de los 500 Hz deseados, pero
eso es algo que se prefiere evitar normalmente. Más adelante se verá cómo
usar un oscilador externo (Cristal) para poder obtener resultados más precisos.

La practica como siempre la tienes en este hipervínculo.

Practica 11: PWM calculado, parte 2 (Mayor frecuencia de


operación en el microcontrolador).
Muchas veces, para algunos de nuestros gustos, 1.1 MHz es demasiado lento,
el microcontrolador ofrece entonces más modalidades de velocidad, siendo
la máxima 16 MHz.

El hecho de cambiar la frecuencia de reloj del microcontrolador solo cambia


el número de frecuencia_micro en la formula denotada anteriormente.

A continuación muestro el siguiente programa con el código para funcionar a


8 MHz:

//Programa para generar un PWM de aproximadamente 2 milisegundos de periodo


//Ahora con un reloj interno de 8 MHz.

#include "msp430x21x2.h"
volatile unsigned int res;
TI MSP430

int main( void )


{
unsigned char d;
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
P á g i n a | 98

if (CALBC1_8MHZ ==0xFF || CALDCO_8MHZ == 0xFF)


{
while(1); // If calibration constants erased
// do not load, trap CPU!!
}
BCSCTL1 = CALBC1_8MHZ; // Set DCO to 8MHz
DCOCTL = CALDCO_8MHZ;

/***********************************************Inicio Configuracion ADC*/


ADC10CTL1 = INCH_0 + ADC10DIV_0;
ADC10CTL0 = SREF_2 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilize.
/***********************************************Fin configuracion ADC*/

P1DIR = 0x0F; // P1.2 and P1.3 output


P1SEL |= 0x04; // P1.2 and P1.3 TA1/2 otions

TA0CCR0 = 16000; // PWM Period

/*Calculo de la frecuencia y periodo de un PWM.


TA0CCR0 realmente es solo un contador que se mueve a la velocidad del
micro, así, sin configuraciones extras el micro corre a 1.1 MHz.
De tal manera que 2 miliseguindos = 0.002 segundos... entonces para el
calculo del periodo: 0.002/(1/8000000)=16000
Recordar que f=1/T y T=1/f.
*/

TA0CCTL1 = OUTMOD_7; // TA0CCR1 reset/set


TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode
P1OUT&=0xF4;
__bis_SR_register(GIE); // Habilitar interrupciones

while(1)
{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
/*******************************************Fin Captura valor ADC*/
TA0CCR1 = res; // TA0CCR1 PWM duty cycle
}

return 0;
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


__interrupt void ADC10_ISR(void)
{
res = ADC10MEM * 16; // De aqui obtenermos el resultado del ADC.
// Multiplicamos por 16 para que alcance todo
// el rango del periodo para PWM.
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}
TI MSP430

Video del programa funcionando.

Se puede apreciar que al código realmente solo se le agregaron las siguientes


líneas y cambiaron unos números:
P á g i n a | 99

Donde la única funcionalidad del if, es verificar si los registros de calibración


están correctos (se encargan de manejar la velocidad de los relojes del micro),
y si lo están procede entonces a configurarse a 8 MHz.

Las instrucciones como esta vienen definidas en el archivo include que


estamos usando:
TI MSP430
P á g i n a | 100

En el video se muestra cómo es que a pesar de que tenemos un PWM de


500Hz (2 ms) la salida se muestra algo inestable, esto es debido a la
configuración del ADC.

El programa en si sirve para demostrar el uso de más frecuencias además de


las que el micro presenta por default, en la siguiente practica se resolverán los
problemas tanto del ADC como de la imprecisión del reloj interno del
microcontrolador.

El programa de la práctica la tienes en este enlace.

Practica 12: PWM calculado, parte 3 (Uso de un cristal externo


+ configuración ADC alterna).
En la práctica anterior se pudo apreciar las deficiencias de usar el reloj interno
del microcontrolador, la idea de esta práctica es usar un cristal los cuales son
ampliamente conocidos por su precisión. La configuración física del
microcontrolador vendría a ser como en la siguiente imagen:
TI MSP430

Y a continuación el programa:

//Programa para obtener un PWM en el pin P1.2, el microcontrolador depende de un


//Cristal externo conectados en los pines 5 y 6 del mismo (8Mhz), se usa el
P á g i n a | 101

//Analog 0 para controla el duty cycle del PWM, y se requiere referencia externa
//de 3v en el pin 2.4.
#include "msp430x21x2.h"
unsigned int res, d;
unsigned char i;

int main( void )


{

// Stop watchdog timer to prevent time out reset


WDTCTL = WDTPW + WDTHOLD;

BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8


// con DIVA_3 hacemos que el relog auxiliar
// ACLK sea la frecuencia del cirstal dividida
// entre 8

BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz


do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1

/***********************************************Inicio Configuracion ADC*/


ADC10CTL1 = ADC10SSEL_3 + INCH_0 + ADC10DIV_0;
//ADC10SSEL_3 indica seleccion de reloj
//esclavo primario SMCLK.

ADC10CTL0 = SREF_2 + ADC10SHT_2 + ADC10ON + ADC10IE;


//Bajando los ciclos de reloj para
//Sample and hold demostro tener
//Mejores resultados

__bis_SR_register(GIE); // Habilitar interrupciones


/***********************************************Fin configuracion ADC*/

P1DIR = 0x0F; // P1.2 output


P1SEL |= 0x04; // P1.2 funcion secundaria (Timer A 0.1)

TA0CCR0 = 16000; // PWM Period


TA0CCTL1 = OUTMOD_7; // TA0CCR1 reset/set
TA0CTL = TASSEL_2 + MC_1; // SMCLK, up mode

P1OUT&=0xF4;

while(1)
{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
TI MSP430

/*******************************************Fin Captura valor ADC*/


for( d = 1200; d > 0; d-- ); // Retraso para permitir mas estabilidad en el PWM.
// Multiplicamos por 16 para que alcance todo
// el rango del periodo para PWM.
TA0CCR1 = res;
}
P á g i n a | 102

return 0;
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


__interrupt void ADC10_ISR(void)
{
res = ADC10MEM * 16; // De aqui obtenermos el resultado del ADC.
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}

Video del programa funcionando.

El programa muestra indicios de ser muy similar a los anteriores excepto por las
siguientes líneas:

Dado el control de los microcontroladores Texas Instruments, estos nos permiten


configurar inclusive a que relojes queremos redireccionar el cristal; el micro por
si solo posee cuatro relojes, los cuales son:

Reloj principal MCLK.


Reloj esclavo principal SMCLK.
Reloj auxiliar ACLK.
Reloj del dispositivo a usar, por ejemplo para un ADC ADC10OSC.

Cada uno de ellos puede ser configurado dependiendo de las necesidades


que se presenten. El único reloj que es base para todos es MCLK, de ahí, SMCLK
TI MSP430

y ACLK pueden ser controlados a la misma frecuencia de MCLK, divisiones de


frecuencia del MCLK o relojes externos al microcontrolador.
P á g i n a | 103

Normalmente para cada periférico del micro se puede escoger la fuente de


reloj, el mismo servirá para el funcionamiento del mismo; observando los
registros de cada periférico se puede observar que bit o bits configuran el reloj
a elegir, por ejemplo para el ADC, el registro encargado de configurar la
fuente del reloj es ADC10CTL1 y la instrucción para poder seleccionar el reloj es
ADC10SSEL_X donde X puede ser un valor del 0 al 3 (en el archivo
msp430x21x2.h mencionado anteriormente se puede ver que opciones
selecciona ADC10SSEL.

Los parámetros que se configuraron para el uso del ADC cambiaron un poco
para hacer más estable al PWM.

La instrucción for(d=1200;d>0;d--); solo es un delay que permite una mayor


estabilidad al PWM, dando tiempo para almacenar correctamente el
resultado del ADC en la variable global res y posteriormente colocándola en el
TA0CCR1 para cambiar el PWM acorde al nivel de voltaje que tengamos en el
pin A0 del micro. El resultado de esta nueva configuración son 500 Hz estables.

Aplicación práctica de este programa (Servo controlado por PWM).


TI MSP430

Y finalmente el archivo de la practica… aquí.

Con esto se concluye el manejo de los timers para el uso de PWM.


P á g i n a | 104

Practica 13: Interrupciones por Timers.


Dejando de lado un poco los PWMs, los timers tienen otras funciones bastante
interesantes, como lo son las interrupciones.

Una interrupción es una petición que, cuando se le hace un microcontrolador


o un microprocesador, deja de hacer lo que estaba haciendo y ejecuta una
sección de código especificada por el usuario.

En el siguiente ejemplo se llama a dos interrupciones usando los timers TA0.0 y


TA0.1, cuando la interrupción TA0.0 es ejecutada, el código de la interrupción
cambia el estado del pin P1.0 (si es 0 lo cambia a 1 y viceversa), esta
interrupción es llamada cada 2 milisegundos (500 Hz); a su vez TA1.0 llama a
una interrupción cada 1 milisegundo (1 KHz) la cual cambia el estado del pin
P1.1.

El código del programa es este:

#include "msp430x21x2.h"

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
unsigned int i;
//******************************************Inicio configuracion cristal
BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
//********************************************Fin configuracion cristal

P1DIR = 0x0F;
P1SEL = 0x00; // Sin funcion secundaria el puerto 1
P1OUT &= 0x00; // Limpiar puerto 1

TA0CCTL0 = CCIE; // TA0CCR0 interrupt enabled


TA0CCR0 = 16000; // Periodo TA0CCR0
TA0CTL = TASSEL_2 + MC_1; // SMCLK + UP MODE (TO CCR0)

TA1CCTL0 = CCIE; // TA0CCR1 interrupt enabled


TA1CCR0 = 8000; // Periodo TA0CCR1
TA1CTL = TASSEL_2 + MC_1; // SMCLK + UP MODE (TO CCR1)

__bis_SR_register(GIE); // Interrupt enable


TI MSP430

while(1)
{
for(i=0;i<=10000;i++);
switch((P1IN&0x04))
P á g i n a | 105

{
case 0x04: P1OUT &=~0x04; break;
case 0x00: P1OUT |=0x04; break;
}
}
}

//Timer0_A0 interrupt service routine


#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0(void)
{
switch((P1IN&0x01))
{
case 0x01: P1OUT &=~0x01; break;
case 0x00: P1OUT |=0x01; break;
}
}

//Timer1_A0 interrupt service routine


#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer_A1(void)
{
switch((P1IN&0x02))
{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; break;
}
}

Video del programa en funcionamiento.

Se puede apreciar que en el canal 3, la onda rosa esta desincronizada a


comparación de las otras dos (azul y amarilla), esto es debido a que el pin P1.2
realmente solo está corriendo bajo el ciclo infinito while del programa.

Si bien por algo se caracterizan las interrupciones por timer es que son
extremadamente precisas, y pueden ayudarnos por ejemplo en situaciones
donde se requiera precisión como lo son filtros digitales ó controles.

Cosas a remarcar en este programa:


TI MSP430
P á g i n a | 106

24
TI MSP430

El programa aquí.

24
¿Bonito osciloscopio… no?
P á g i n a | 107

Practica 14: Interrupciones por Timers externos.


El subtitulo puede no decir mucho, pero en realidad describe una situación a
la cual alguna vez nos hemos enfrentado, sea leer un encoder25 incremental o
simplemente leer y/o medir una cantidad de pulsos. Esto se puede hacer
fácilmente con los timers y sus respectivas interrupciones.

El siguiente programa describe una situación donde se tiene un motor, el cual


tiene un encoder que genera una cantidad x de pulsos, cada 1000 pulsos una
interrupción se activa, misma que a su vez incrementa el valor de una variable
d que nosotros declaramos. El programa corre normalmente desplegando el
valor de d en los primeros 4 bits del puerto 1; d solo es incrementada cuando la
interrupción ha sido activada.

Código:

//Programa para uso de timer externo como activador de eventos.


//El microcontrolador recibe pulsos por el pin 2.1, cada vez que el contador del
//del timer externo alcanza el valor de TA0CCR0 una interrupcion es activada
//Esta lo unico que hace es incrementar el valor de d en 1.
#include "msp430x21x2.h"
unsigned int d;

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
unsigned int i;
d=0;
//******************************************Inicio configuracion cristal
BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
//********************************************Fin configuracion cristal

P1DIR = 0x0F;
P1SEL = 0x00; // Sin funcion secundaria el puerto 1
P1OUT &= 0x00; // Limpiar puerto 1
P2SEL |= 0x02; // Funcion secundaria en el pin P2.1
P2DIR &= ~0x02; // P2.1 como entrada.

TA0CCTL0 = CCIE; // TA0CCR0 interrupt enabled


TA0CCR0 = 1000; // Rango TA0CCR0
TA0CTL = TASSEL_3 + MC_1; // TAINCLK + UP MODE (TO CCR0)
TI MSP430

__bis_SR_register(GIE); // Interrupt enable

25
http://en.wikipedia.org/wiki/Rotary_encoder
P á g i n a | 108

while(1)
{
if(d>=16){d=0;}
P1OUT=d;
//Si quisieramos saber el numero de pulsos que tenemos en TAINCLK
//Podemos usar la variable TA0R para llamar al valor.
//Ejemplo n=TA0R;
}
}

//Timer0_A0 interrupt service routine


#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0(void)
{
d=d+1;
}

Video del programa en funcionamiento.

Video del programa en funcionamiento mostrando el registro TA0R.

Notas acerca del programa: es básicamente lo mismo que el programa


anterior, la diferencia en esta ocasión es que solo usamos un solo el timer TA0, y
en el mismo usamos la fuente de reloj externa:

Y esa fuente de reloj externa viene dada por nuestro motor, el cual tiene
acoplado un encoder que produce una señal cuadrada:
TI MSP430
P á g i n a | 109

Y cosas a remarcar del programa:

Con esta última práctica se concluye el manejo de los timers.

Practica 15: Interrupciones en los puertos.


Más de una vez nos encontraremos con una situación, en donde si obtenemos
un 1 lógico queremos que el micro deje de hacer lo que está haciendo para
TI MSP430

hacer algo que nosotros queremos.


P á g i n a | 110

Ese es el caso de las interrupciones y más en concreto las interrupciones en los


puertos, ya que con solo activar un pin o desactivarlo podemos dar pie a una
petición al microcontrolador, y de ahí lo que de a pie la imaginación.

Para el siguiente ejemplo usé una situación clásica, controlar el disparo de un


TRIAC26 (Básicamente, un transistor para corriente alterna), su uso es muy
amplio, es como un PWM para corriente alterna.

Para ello, se requiere que tanto en el semiciclo positivo como en el semiciclo


negativo exista un disparo. Suponiendo una onda senoidal de 60 Hz, la imagen
nos puede decir mucho:

Esa resistencia puede ser, ya sea un foco, un ventilador… una carga en sí. Para
ello normalmente se recurre a tomar la misma señal senoidal (de CFE por
ejemplo, 120v 60Hz) reduciéndola y creando pulsos cuadrados con la misma.
TI MSP430

26
http://es.wikipedia.org/wiki/Triac
P á g i n a | 111

Ya con la señal cuadrada se recurre a crear pulsos con la misma, los cuales se
desplazan a lo largo del todo el semiciclo:
TI MSP430
P á g i n a | 112

El programa está diseñado para generar esos pulsos en base a interrupciones;


se necesitan 2 de las mismas para ello, una que detecte el flanco de subida en
la onda cuadrada y otra que detecte el flanco de bajada en la misma,
entonces, se requiere de un pin que detecte dicho flanco de subida y otro que
detecte el de bajada.

Las interrupciones externas en el msp430F2132 vienen dadas por todos los pines
tanto del puerto 1 como del puerto 2, ellos se pueden controlar fácilmente en
el programa mediante el uso de banderas (bits) en registros que representan
cada uno de los pines. A continuación el programa27:

//Programa para generar pulsos siguiendo una onda patron cuadrada de corriente continúa (NO
//alterna, siempre arriba del eje x), sirve como control para un triac de disparo.
#include "msp430x21x2.h"
unsigned int z,i,prom,res;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

//******************************************Inicio configuracion cristal


BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
//********************************************Fin configuracion cristal

//******************************************Inicio configuracion ADC


ADC10CTL1 = ADC10SSEL_3 + ADC10DIV_0; //ADC10SSEL_3 indica seleccion de
reloj
//esclavo primario SMCLK.
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON; //Bajando los ciclos de reloj para
//Sample and hold demostro tener
//Mejores resultados
ADC10AE0 |= 0x01; // P2.0 ADC option select
ADC10DTC1 = 0x01; // 1 conversion
//********************************************Fin configuracion ADC

P1DIR |= 0x0F;
P2DIR &= ~0x06; // P2.1 and P2.2 as input direction
P2IE |= 0x06; // P2.1 and P2.2 interrupt enabled
P2IES &= ~0x02; // P2.2 low to high transition interrupt
P2IES |= 0x04; // P2.2 high to low transition interrupt
P2IFG &= ~0x06; // P2.1 and P2.2 IFG cleared
P1OUT &= 0x00;
TI MSP430

__bis_SR_register(GIE); // Enter LPM4 w/interrupt


while(1)
{

27
Su equivalente exactamente en el PIC18F4450 de microchip.
P á g i n a | 113

ADC10SA = (unsigned int)&res; // Data transfer location


ADC10CTL0 |= ENC + ADC10SC; // Start sampling
}
}

// Port 2 interrupt service routine


#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
for(z=0;z<=(res/8);z++);
P1OUT |= 0x01;
for(z=0;z<=2;z++);
P1OUT &= ~0x01;

P2IFG &= ~0x02; // P2.1 IFG cleared


P2IFG &= ~0x04; // P2.2 IFG cleared

/*
//Código innecesario, solo demuestra el uso de interrupciones individualmente
if((P2IFG&0x06)==0x02) // If Interrupt Flag of 2.1 is activated...
{
for(z=0;z<=(res/16);z++);
P1OUT |= 0x01;
for(z=0;z<=2;z++);
P1OUT &= ~0x01;
P2IFG &= ~0x02; // P2.1 IFG cleared
}
else if((P2IFG&0x06)==0x04) // If Interrupt Flag of 2.2 is activated...
{
for(z=0;z<=(res/8);z++);
P1OUT |= 0x01;
for(z=0;z<=2;z++);
P1OUT &= ~0x01;
P2IFG &= ~0x04; // P2.2 IFG cleared
}
else
{
P2IFG &= ~0x02; // P2.1 IFG cleared
P2IFG &= ~0x04; // P2.2 IFG cleared
}
*/
}

Video del programa en funcionamiento

Antes de explicar un poco las interrupciones se abordará un poco lo que se


hizo con el ADC y algunas otras cosas más en el funcionar del programa:
TI MSP430
P á g i n a | 114

Esta configuración nos permite enviar el dato capturado del ADC


directamente a un registro (o variable), lo cual evita el uso del procesador (y
de una interrupción más del ADC que pueda hacer ruido a las interrupciones
que más nos importan, que son las de los pines P2.1 y P2.2)
TI MSP430
P á g i n a | 115

Cuando una interrupción en el puerto 2 es activada, el registro P2IFG es


cargado con una bandera (siendo un registro de 8 bits cada bit representa a
uno de los pines del puerto 2), esta bandera es la que permite al micro
ejecutar el código de la interrupción. Al principio del programa se borra para
evitar posibles errores debido a una activación no deseada.

Y Finalmente para el código de la interrupción.

Se puede apreciar que hay más código pero que el mismo ha sido
comentado, esto es si se desea tener código individual por cada interrupción
TI MSP430

(jugando con los bits del registro P2IFG se puede lograr esto).
P á g i n a | 116

Con esta práctica se termina el uso de las interrupciones para el


microcontrolador, de ahora en adelante se hablará principalmente de
interfaces.

Interfaces.

Practica 16: El estándar RS232-C (para nosotros simplemente


RS232).
El estándar RS232 lleva desde hace ya mucho tiempo, es un protocolo sencillo,
practico y conformado por pocos cables (en configuración mínima solo se
requieren 2 cables, RX y TX), el programador ez430-RF2500 tiene un puente
RS23228 listo para ser usado (el programador ez430-F2013 también lo tiene, pero
a este hay que hacer una modificación a la EEPROM que será discutida más
adelante)
TI MSP430

28
Para aquellos que no quieran usar este puente, se puede construir uno con un MAX232 y un
adaptador RS232 a USB con este esquemático.
P á g i n a | 117

El RS232 salió más o menos en la misma época que el puerto paralelo, en la


actualidad el puerto paralelo está extinto en un buen numero de
computadoras, pero la razón por la cual el puerto serial (DB9, RS232) ha
permanecido es por la facilidad de uso del mismo (además de la
portabilidad). Hay un sinfín de programas que pueden hacer uso del mismo sin
complicaciones, desde los más sencillos (Hyperterminal) hasta los más
complejos (LabVIEW).

Para este ejercicio usaremos el Hyperterminal (incluido en Windows XP):

Para usuarios de Windows Vista (32 y 64 Bits) la versión completa y gratuita la


puedes encontrar aquí.

Una vez instalado tendremos una pantalla como esta:


TI MSP430
P á g i n a | 118

En nombre se puede poner lo que sea, damos OK y obtendremos una ventana


como esta:
TI MSP430
P á g i n a | 119

En ella seleccionaremos el puerto COM que corresponde a nuestro


programador (se puede ver en el Administrador de Dispositivos de Windows)
para este caso es el COM13. Después de eso aparecerá otra ventana de
configuración:

Finalmente presionaremos aplicar y después aceptar. Lo único que nos


quedará será una pantalla en blanco, en ella podremos ver los mensajes que
mandemos desde el microcontrolador por RS232.
TI MSP430
P á g i n a | 120

La conexión al microcontrolador es de la siguiente manera, esta vez usaremos


los pines mencionados anteriormente para el RS232, TX y RX.

Ahora el programa:

//Programa para desplegar un Hola Mundo usando cualquier cliente RS232 en


//Windows con las teclas 1 y 2, Se recomienda usar hyperterminal.
#include "msp430x21x2.h"
char rx,i;

void TXString( char* string, int length );

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1DIR=0x0F; //4 Leds de salida P1.0-P1.3
//4 dip-switch de entrada P1.4-P1.7
P1OUT=0x00;

//******************************************Inicio configuracion cristal


BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
TI MSP430

//********************************************Fin configuracion cristal

P3SEL = 0x30; // P3.4,5 = USCI_A0 TXD/RXD


UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 0x41; // 8MHz 9600
P á g i n a | 121

UCA0BR1 = 0x03; // 8MHz 9600


UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt

__bis_SR_register(GIE); // Enter LPM0, interrupts enabled

while(1)
{
if(rx=='1')
{
TXString("\n\f Hola",7);
rx='0';
}
else if(rx=='2')
{
TXString("\n\f Mundo",8);
rx='0';
}
}
}

//Interrupcion RX
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
rx = UCA0RXBUF; // TX -> RXed character
//**********************************************Inicio Prende y Apaga P1.1
switch((P1IN&0x02))
{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; break;
}
//**********************************************Fin Prende y Apaga P1.1
}

void TXString( char* string, int length )


{
int pointer;
for( pointer = 0; pointer < length; pointer++)
{
UCA0TXBUF = string[pointer];
//**********************************************Inicio Prende y Apaga P1.0
switch((P1IN&0x01))
{
case 0x01: P1OUT &=~0x01; break;
case 0x00: P1OUT |=0x01; break;
}
//**********************************************Fin Prende y Apaga P1.0
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?
}
}
TI MSP430

Video del programa en funcionamiento.

Los registros necesarios para hacer funcionar el RS232 son:


P á g i n a | 122

P3SEL que indica la segunda función de los pines P3.4 y P3.5 (TX y RX para el
RS232)

UCA0CTL1 nos indica la fuente del reloj para generar los baudios en la
transmisión de datos, puede ser desde 0 hasta 3, la constante UCSSEL_2 nos
deja usar el reloj esclavo principal.

UCA0BR0 y UCA0BR1 son dos registros que forman 1 solo preescaler. Un


preescaler es un divisor de frecuencia que, en esta ocasión nos servirá para
poder dar las velocidades estándares que se necesiten, por ejemplo una de
ellas es la de 9600 baudios (baudios= símbolos/segundo) que es la que ha sido
en el programa (se pueden velocidades más altas pero debido a limitante del
código en la EEPROM del programador solo es posible enviar datos hasta 9600
baudios).

El programa está configurado para usar un cristal de 8 MHz, esto quiere decir
8000000 Hz. UCA0BR1 es la parte alta de un numero, mientras que UCA0BR0 es
la parte baja del mismo, como se puede ver en la imagen estos corresponden
a los valores de 0x03 y 0x41 respectivamente en hexadecimal. Juntándolos el
número en si sería 0x0341 cuya conversión en decimal es 833, si dividimos eso
entre los 8 Mhz nos quedaría algo como:

Que son aproximadamente los 9600 baudios que requerimos.

UCA0MCTL controla la modulación de los pulsos en RX, se recomienda leer la


hoja de datos para algún uso especifico de este registro (Particularmente he
TI MSP430

probado diversas velocidades y los resultados con la modulación 1 son los


esperados).

UCA0CTL1 se usa para inicializar el modulo RS232 quitando el bit UCSWRST.


P á g i n a | 123

IE2 nos da la opción a habilitar la interrupción RX, muy importante cuando se


tiene una comunicación bidireccional, puesto que detecta cuando una tecla
ha sido oprimida del lado de la computadora.

Es importante decir que se declaró una función llamada TXString, tomada de


los mismos ejemplos referentes al RS232. La función simplifica mucho del
trabajo que hay que hacer para enviar varias secuencias de caracteres o
números por el RS232 (También conocidas como cadenas o strings en inglés).

Lo que está en rojo representa solamente la función, lo que no lo está es solo


un código usado para prender y apagar el LED P1.0 cada vez que se envié un
solo dato.

La función toma una cadena de caracteres y además su tamaño, entonces


mediante un ciclo for determinado por el tamaño del string, envía caracteres
secuencialmente uno por uno por el RS232.

UCA0TXBUF es el registro que se encarga de enviar el dato (Un pequeño buffer


TI MSP430

el cual una vez se le es cargado el dato este es enviado rápidamente por el


pin TX del microcontrolador)
P á g i n a | 124

La instrucción while (!(IFG2&UCA0TXIFG)); actúa como un switch que mantiene


al procesador ocupado mientras envía el dato que fue colocado en el buffer,
es recomendable usar esta instrucción dentro de la función para evitar
mandar datos rápidamente al buffer sin haber sido enviado los anteriores,
evitando posibles errores.

La interrupción en RX nos ayuda a detectar cuando un mensaje le ha llegado


desde la computadora usando el protocolo RS232 al microcontrolador, todo lo
que está en rojo representa la interrupción y lo que no es solo para prender y
apagar el pin 1.1, indicando que un dato ha llegado a RX mediante la PC.

Volcando el registro UCA0RXBUF a una variable global char declarada por


nosotros se puede hacer uso del dato que ha llegado desde la PC, esto para
ejecutar cualquier secuencia de código que se requiera.

El programa solo consiste de dos sentencias if, cuya única función es comparar
el dato que ha sido recibido desde la computadora, dado que estos
normalmente son representados en código ASCII29, para mencionar que
queremos comparar un 1 debemos colocarlo entre apostrofes, de tal manera
TI MSP430

que se muestre como ‘1’ cuando esperamos comparar un 1 proveniente del

29
http://es.wikipedia.org/wiki/ASCII para más información.
P á g i n a | 125

teclado, otro ejemplo es para una a, en la cual la tendríamos que escribir


también entre apostrofes de tal manera que aparezca como ‘a’.

Casi la misma regla aplica al usar la función TXString, donde el string o la


cadena de datos que enviemos tenemos que colocarla entre comillas, por
ejemplo para enviar un hola mundo la sintaxis con todo y función sería así:
TXString(“Hola Mundo”,10);, Hola ocupa 4 caracteres, el espacio es uno más y
Mundo son 5 caracteres, dando un tamaño de 10, cuyo número es colocado
en el segundo argumento de la función.

En el código ASCII además hay unos comandos especiales muy útiles, en C


estos comandos se representan mediante una diagonal y alguna letra, como
lo son:

\f Limpia la pantalla entera

\r Retorno de línea (regresa el cursor al principio de la línea)

\n Salto de línea (El equivalente a dar un ENTER en un procesador de textos)

Cada uno de estos se toma como un solo carácter. La razón para usar esos
comandos es porque si nosotros colocamos un TXString(“Hola”,4); tendríamos
algo como esto:

No dejando espacio para otros datos a desplegar en pantalla.


TI MSP430

Las posibilidades para el RS232 son inmensamente grandes, el siguiente


programa mostrará una de ellas, la cual es la función de termómetro, el
código no se explicará salvo mínimos detalles ya que la mayoría del mismo ha
sido discutido con anterioridad:
P á g i n a | 126

//Programa para desplegar la temperatura por RS232 en el Hyperterminal o


//Cualquier otro cliente RS232, presionando la tecla 1 en el teclado
//La tecla 2 muestra un mensaje y cualquier otra tecla limpia la pantalla.

#include "msp430x21x2.h"
char rx,i,d,c=0;
char t[]={"Temp= XXXX C \r"};
char a[]={"Sensor de temperatura, 40 muestras promediadas \r\n"};
volatile long res,res2;
long long prom=0, suma=0;

void TXString( char* string, int length );

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1DIR=0x0F; //4 Leds de salida P1.0-P1.3
//4 dip-switch de entrada P1.4-P1.7
P1OUT=0x00;

//******************************************Inicio configuracion cristal


BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
//********************************************Fin configuracion cristal

P3SEL = 0x30; // P3.4,5 = USCI_A0 TXD/RXD


UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 0x41; // 8MHz ~ 9600
UCA0BR1 = 0x03; // 8MHz ~ 9600
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt

//*********************************************Configuracion ADC sensor temperatura


ADC10CTL1 = INCH_10 + ADC10DIV_0;
ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilice.
//******************************************Fin Configuracion ADC sensor temperatura

__bis_SR_register(GIE); // Enter LPM0, interrupts enabled

while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Start sampling
TI MSP430

if( res2 < 0 ) //Se detecta si se debe poner un signo negativo


{
t[6] = '-';
res2 = res2 * -1;
}
else if( prom >= 0 ) //Aqui es para detectar si se tiene un 4to
P á g i n a | 127

//digito... por ejemplo en 1023 el 1 es el cuarto


{
t[6] = '0'+((res2/1000)%10);
}

t[9] = '0'+(res2%10); //ese %10 cambia de lo que sea a decimal (en este caso binario),
// permitiendo un despliegue de informacion
t[8] = '0'+((res2/10)%10); //en decimales, ademas de que aqui se divide
//cada parte del numero entre 10 para obtener
t[7] = '0'+((res2/100)%10); //cada caracter en unidades, decenas y centenas

while (BUSY & ADC10CTL1); //Espera a que el modulo ADC 10 termine por completo de
//Apagare

if(rx=='1')
{
TXString(t,sizeof(t));
}
else if(rx=='2')
{
TXString(a,sizeof(a));
}
else
{
TXString("\f",1); //Cualquier otra cosa limpia la pantalla
}
}
}

//Interrupcion RX
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
rx = UCA0RXBUF; // TX -> RXed character
//**********************************************Inicio Prende y Apaga P1.1
switch((P1IN&0x02))
{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; break;
}
//**********************************************Fin Prende y Apaga P1.1
}

void TXString( char* string, int length )


{
int pointer;
for( pointer = 0; pointer < length; pointer++)
{
UCA0TXBUF = string[pointer];
//**********************************************Inicio Prende y Apaga P1.0
switch((P1IN&0x01))
{
case 0x01: P1OUT &=~0x01; break;
TI MSP430

case 0x00: P1OUT |=0x01; break;


}
//**********************************************Fin Prende y Apaga P1.0
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?

}
P á g i n a | 128

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


//la conversión.
__interrupt void ADC10_ISR(void)
{
res=ADC10MEM;
ADC10CTL0 &= ~ENC; // Apagamos el ADC

//***********************************inicio promedio de 40 muestras de temperatura


suma=suma+res;
c++;
if(c>=40)
{
c=0;
prom=suma/40;
res2 = prom*423/1024 - 278; //Resultado en grados centígrados
suma=0;
}
//***********************************fin promedio de 40 muestras de temperatura
}

Video con el programa en funcionamiento.

Detalles del programa:

Se pueden declarar strings completos usando los {} y los “”, esto es muy útil
cuando se requiere cambiar únicamente algunas letras del mensaje, como
para por ejemplo colocar los números de la temperatura, además de
simplificar el uso de la función TXString, que para enviar la constante entera
solo basta con teclear TXString(a, sizeof(a)); mandando la variable y su
tamaño automáticamente.
TI MSP430
P á g i n a | 129

Res2 es nuestra variable con el resultado ya convertido en grados. Para


convertir un número a algo que pueda ser enviado por RS232 se requiere
descomponerlo en unidades, decenas, centenas… etc, y transformar cada
uno de esos números en un “carácter” para que pueda ser enviado por RS232.

En la parte superior declaramos la variable char t[]={"Temp= XXXX C \r"}; esta


puede ser vista de la siguiente manera Res2 es nuestra variable con el
resultado ya convertido en grados. Para convertir un número a algo que
pueda ser enviado por RS232 se requiere descomponerlo en unidades,
decenas, centenas… etc, y transformar cada uno de esos números en un
“carácter” para que pueda ser enviado por el protocolo serial.

Al declarar t[7]=’0’+((res2/100)%10); lo que hacemos es reemplazar


exactamente la X del carácter 7 por un 0 más una constante que viene dada
TI MSP430

por la división de res2 entre 100 (un numero que representa las centenas de
P á g i n a | 130

res2), lo mismo con las unidades y decenas; convirtiendo efectivamente el


numero en particular en un carácter que se puede usar en RS232 30.

Usando LabVIEW 8.2 (Aunque sirve igual con otras versiones) con la
práctica del termómetro anterior ligeramente modificada.
LabVIEW es un entorno de programación grafico muy intuitivo, quizás de los
mejores sets de programación que he trabajado ya que permite de una
manera muy simple y visual crear programas elaborados. Pero primero que
nada el programa para el microcontrolador:

//Programa para desplegar la temperatura por RS232 en el Hyperterminal o


//Cualquier otro cliente RS232, presionando la tecla 1 en el teclado
//La tecla 2 muestra un mensaje y la tecla 3 limpia la pantalla.

#include "msp430x21x2.h"
char rx,i,d,c=0;
char t[]={"\r\nTemp= XXXX C"};
char a[]={"\r\nSensor de temperatura, 40 muestras promediadas"};
volatile long res,res2;
long long prom=0, suma=0;

void TXString( char* string, int length );

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P1DIR=0x0F; //4 Leds de salida P1.0-P1.3
//4 dip-switch de entrada P1.4-P1.7
P1OUT=0x00;

//******************************************Inicio configuracion cristal


BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
//********************************************Fin configuracion cristal

P3SEL = 0x30; // P3.4,5 = USCI_A0 TXD/RXD


UCA0CTL1 |= UCSSEL_2; // SMCLK
UCA0BR0 = 0x41; // 8MHz ~ 9600
UCA0BR1 = 0x03; // 8MHz ~ 9600
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
TI MSP430

//*********************************************Configuracion ADC sensor temperatura

30
Y pensar que se usaba printf(“Temp= %d”,res2); en un PIC…
P á g i n a | 131

ADC10CTL1 = INCH_10 + ADC10DIV_0;


ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilice.
//******************************************Fin Configuración ADC sensor temperatura

__bis_SR_register(GIE); // Enter LPM0, interrupts enabled

while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Start sampling

if( res2 < 0 ) //Se detecta si se debe poner un signo negativo


{
t[8] = '-';
res2 = res2 * -1;
}
else if( prom >= 0 ) //Aquí es para detectar si se tiene un
//4to digito... por ejemplo en 1023 el 1 es el cuarto
{
t[8] = '0'+((res2/1000)%10);
}

t[11] = '0'+(res2%10); //ese %10 cambia de lo que sea a decimal (en este caso binario),
// permitiendo un despliegue de información
t[10] = '0'+((res2/10)%10); //en decimales, además de que aquí se divide
//cada parte del numero entre 10 para obtener
t[9] = '0'+((res2/100)%10); //cada carácter en unidades, decenas y centenas

while (BUSY & ADC10CTL1); //Espera a que el modulo ADC 10 termine por completo de
//Apagarse

if(rx=='1')
{
TXString(t,sizeof(t));
}
else if(rx=='2')
{
TXString(a,sizeof(a));
}
else if(rx=='3')
{
TXString("\f",1); //Un 3 limpia la pantalla
}
}
}

//Interrupcion RX
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
rx = UCA0RXBUF; // TX -> RXed character
//**********************************************Inicio Prende y Apaga P1.1
switch((P1IN&0x02))
TI MSP430

{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; break;
}
//**********************************************Fin Prende y Apaga P1.1
}
P á g i n a | 132

void TXString( char* string, int length )


{
int pointer;
for( pointer = 0; pointer < length; pointer++)
{
UCA0TXBUF = string[pointer];
//**********************************************Inicio Prende y Apaga P1.0
switch((P1IN&0x01))
{
case 0x01: P1OUT &=~0x01; break;
case 0x00: P1OUT |=0x01; break;
}
//**********************************************Fin Prende y Apaga P1.0
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?

}
}

#pragma vector=ADC10_VECTOR //Interrupcion del ADC (Activada cuando se completa


//la conversión.
__interrupt void ADC10_ISR(void)
{
res=ADC10MEM;
ADC10CTL0 &= ~ENC; // Apagamos el ADC

//***********************************inicio promedio de 40 muestras de temperatura


suma=suma+res;
c++;
if(c>=40)
{
c=0;
prom=suma/40;
res2 = prom*423/1024 - 278; //Resultado en grados centú„rados
suma=0;
}
//***********************************fin promedio de 40 muestras de temperatura
}

El programa por sí solo no muestra muchos cambios, lo único que se cambio


en él fue esto:

\r\n son caracteres que representan “regresar a principio de línea” y “salto de


línea” respectivamente, estos caracteres pueden ser interpretados por
LabVIEW como tales permitiéndonos un manejo más eficiente de los datos
TI MSP430

recibidos por el programa. Para el manejo de los datos en el mismo abriremos


la ventana principal de LabVIEW:
P á g i n a | 133

Seleccionaremos la opción remarcada en rojo, Find Examples… y nos mostrará


una pantalla como esta:
TI MSP430
P á g i n a | 134

Damos clic en la pestaña search y nos mostrara otra pantalla similar a esta:

Entonces primero tecleamos rs en la casilla de Enter Keyword(s), después


seleccionamos RS-232 en la lista y finalmente damos doble clic en Advanced
Serial Write and Read.vi. Esto nos llevará a otra ventana, ya con el ejemplo
abierto:
TI MSP430
P á g i n a | 135

Configura el programa de esta manera:


TI MSP430
P á g i n a | 136

Una vez que estés en el paso cuatro y el botón sea presionado se podrá
apreciar en la pantalla inferior el mensaje que estamos enviando, si todo es
correcto se desplegará exactamente igual que en el Hyperterminal, logrado
eso podemos manipular los datos de acuerdo a nuestras necesidades.

Para modificar el programa necesitamos entrar al diagrama de bloques, para


ello para el programa con:
TI MSP430
P á g i n a | 137

Y después con presionar Ctrl+E ó con el mouse en uno de los menús:

Nos llevará al diagrama a bloques:


TI MSP430
P á g i n a | 138

Hay más código en toda la pagina, pero nos centraremos únicamente en el


ciclo while (es el que viene dado por un rectángulo gris trazado por una
flecha) ya que al igual que en los microcontroladores es un código que se está
ejecutando continuamente.

En el mismo código, lo que nos importa es tomar los datos capturados del
RS232, para ello nos centraremos únicamente en el rectángulo:

Y lo primero que haremos es agrandarlo mediante los pequeños cuadrados


azules que aparecen en el mismo:
TI MSP430
P á g i n a | 139

Ya que tenemos espacio para trabajar, ahora si podemos usar los datos
recibidos. Presiona de nueva cuenta para ejecutar el programa y vuelve al
diagrama de bloques, posiciónate en el cable que está conectado a “read
string” y da un clic sobre el mismo:
TI MSP430
P á g i n a | 140

De esta manera se puede apreciar sobre que hilo van los datos recibidos del
RS232, ahora, para hacer un medidor realmente solo requerimos del número
en sí, no del texto, así que para paramos el programa y usamos:

Colocándolo y conectándolo dentro del mismo cuadro de la siguiente


manera:
TI MSP430
P á g i n a | 141

Ahora colocaremos un indicador para asegurarnos que hemos extraído la


porción del dato que nos interesaba:
TI MSP430
P á g i n a | 142

Entonces volvemos a correr el programa y colocamos una punta de prueba


para observar lo que se extrajo de la cadena original:
TI MSP430
P á g i n a | 143

Como se muestra en la imagen el resultado fue exitoso, es entonces tiempo de


poner indicadores más atractivos visualmente. Para ello paramos el programa
y nos colocamos sobre la plantilla sobre la cual estábamos originalmente (y
acomodamos el indicador que colocamos anteriormente):

Como extra podemos renombrar nuestro indicador:


TI MSP430
P á g i n a | 144

Entonces colocamos un Waveform Chart:

Y un indicador parecido a un termómetro:


TI MSP430
P á g i n a | 145

Finalmente acomodamos para tener una mejor presentación:


TI MSP430

Entonces volvemos al diagrama de bloques y cambiamos el formato del


número, de cadena de caracteres a un número entendible para la
computadora como tal:
P á g i n a | 146

Acomodamos y conectamos los dos indicadores extras que justo acabamos


de colocar:
TI MSP430
P á g i n a | 147

Y para terminar, volvemos a ejecutar el programa:


TI MSP430
P á g i n a | 148

Quizás no sea del todo vistosa la grafica que se muestra, pero eso se puede
arreglar de la siguiente manera: detenemos el programa, colocamos el
puntero del mouse encima de la grafica y seleccionamos properties:
TI MSP430
P á g i n a | 149

Entonces nos colocamos en la pestaña scale y seleccionamos el eje y:


TI MSP430
P á g i n a | 150

Deseleccionamos la opción Autoscale y escribimos la escala que nosotros


deseemos mostrar:
TI MSP430
P á g i n a | 151

Damos OK y volvemos a ejecutar el programa, ahora este se mostrará con una


escala en Y no variable y bastante apreciable:
TI MSP430

Esto concluye el uso de LabVIEW por RS232, guardamos una copia del
programa para no sobre escribir sobre el ejemplo proporcionado por el mismo
(paramos el programa primero):
P á g i n a | 152

Seleccionamos la primera opción:

Guardamos y cerramos LabVIEW.

Video de la práctica en funcionamiento.

Practicas hechas en IAR aquí.

Fin del uso del RS232 con el microcontrolador.


TI MSP430

Practica 17: El protocolo I2C.


Al principio quizás una de las cosas que se debió haber mencionado es que
existen dos clases de protocolos seriales: los asíncronos (no dependen de una
P á g i n a | 153

señal de reloj para funcionar) y los síncronos (aquellos que aparte tienen una
línea más para enviar la señal de reloj)

El protocolo I2C31 entra en la última categoría pero enviando y recibiendo la


información por un solo hilo. Este protocolo es normalmente visto en ambientes
industriales, pero también en ambientes muy diminutos como lo son los
termómetros digitales en una computadora o memorias EEPROM.

Existen varios ejemplos al respecto de cómo usar este protocolo dentro de los
ejemplos que proporciona la misma Texas Instruments, sin embargo aquí se
abordará uno más apegado a la realidad: Como volver un programador
ez430-2013 a un programador ez430-rf2500 ó lo que es lo mismo agregando el
puente USB-RS232 al programador azul.

El método para ello está basado en el trabajo de Travis Godspeed 32 sin cuyos
principios no hubiera sido posible esto, así como el programa que se usará en
IAR hecho por Jorge Alberto Soto Cajiga (Profesor Investigador, CIDESI).

Ingredientes:

 1 programador ez430-F2013 (6 PINES):


TI MSP430

31
http://es.wikipedia.org/wiki/I%C2%B2C

32
http://travisgoodspeed.blogspot.com/2008/05/repurposing-ti-ez430u-part-1.html
P á g i n a | 154

 Un programador JTAG que pueda ser usado por el programa MSP430


FLASH PROGRAMMING UTILITY33:

 Cautín, soldadura, pasta, cables, algunos pines sueltos… etc.

 MSP430 FLASH PROGRAMMING UTILITY.

 IAR Embedded Workbench.

Lo primero que se procede a hacer es a destapar el programador, y una vez


accedido a los pines de prueba podemos acoplar el programador JTAG en
ellos de la siguiente manera:

Es decir:
TI MSP430

33
http://kurt.on.ufanet.ru/
P á g i n a | 155

Para alimentar el circuito es conveniente hacerlo por USB, basta con poner un
papelito entre los dos pines de en medio del conector para que D+ y D- no
hagan contacto y así el programador no sea reconocido en Windows pero si
alimentado. Hecho eso el programador JTAG está listo para reprogramar al
microcontrolador MSP430F1612 (el chip de 64 pines, justo debajo del TUSB3410).

Los programadores ez430-F2013 y ez430-RF2500 usan de puente USB el chip


TUSB3410. Este chip en particular puede operar solamente por si solo como
puente RS232-USB, pero si encuentra una memoria EEPROM conectada al
mismo el chip TUSB3410 leerá el programa contenido en ella. En el
programador F2013 se incluye un puerto serie también, sin embargo es de uso
reservado únicamente para programar el microcontrolador (no se puede
hacer uso de él) mientras que en los programadores RF2500 se da completo
acceso al mismo en programas como Hyperterminal ó LabVIEW.

Eso nos dice que cambiando el código de la EEPROM del programador azul
por el código que contiene la EEPROM del programador rojo, el ez430-F2013
cambiara a ser ez430-RF2500 de una manera transparente.

Observando la hoja de datos34 del programador rojo se puede apreciar cómo


tanto la EEPROM, como el puente USB-RS232 TUSB3410 así como el
microcontrolador MSP430F1612 están interconectados mediante la interface
I2C:
TI MSP430

34
PDFs\slau227c rf2500 USB Rojo Userguide.pdf
P á g i n a | 156
TI MSP430

Lo cual nos vuelve a dar una idea de que con el microcontrolador


MSP430F1612 es posible reprogramar la memoria EEPROM con el código del
programador rojo.
P á g i n a | 157

Extraer los datos de la memoria EEPROM contenida en el programador rojo es


algo que solo es necesario realizar una vez (Lo mismo para el código
contenido en el micro tanto para el rojo como el azul), por ello solo se
abarcará la escritura de la memoria EEPROM y del microcontrolador
integrados en el ez430-F2013, esto para que sean convertidos a otros similares a
los que son incluidos en el programador rojo.

El uso del IAR con el nuevo programador solo cambia en la selección del
microcontrolador, que para este caso será el MSP430F1612:

Lo primero que se hará será reprogramar la EEPROM del programador ez430-


F2013, para ello recurriremos a dos programas (ya que en el código se
TI MSP430

encuentra integrado byte por byte la programación de los mismos, lo mismo


conlleva el uso entero de la RAM, es por eso que tiene que ser dividido en dos).

Para los primeros 3Kb de la EEPROM (código NO FUNCIONAL, solo para


describir el programa, más adelante se colocará el programa para IAR):
P á g i n a | 158

//Este programa escribe los primeros 3 kbs de código de la eeprom


//Esto si queremos que el programador azul se transforme en uno rojo
//Agregando un puerto serial que se puede usar libremente.

//Jorge Alberto Soto.

#include "msp430x16x.h"

/*--- external functions of file "I2Croutines.c" ----------------------------*/


extern void InitI2C(void);
extern void EEPROM_ByteWrite(unsigned int Address,unsigned char Data);
extern unsigned char EEPROM_RandomRead(unsigned int Address);
extern unsigned char EEPROM_CurrentAddressRead(void);
extern void EEPROM_AckPolling(void);
/*---------------------------------------------------------------------------*/

char dat1[1024] = {
16 ,
52 ,
7 ,
0 ,
24 ,
217 ,
2 ,
0 ,

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.

120 ,
78 ,
238 ,
246 ,
18
};
char dat2[1024] = {
1 ,
214 ,
233 ,
162 ,
231 ,

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.

131 ,
240 ,
34 ,
128 ,
240
};
char dat3[1024] = {
TI MSP430

120 ,
78 ,
166 ,
24 ,
8 ,
166 ,
P á g i n a | 159

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.

224 ,
246 ,
163
};

void main(void)
{
P1DIR = 0x00; // termination of unused pins
P2DIR = 0x00;
P3DIR = 0x00;
P4DIR = 0x00;
P5DIR = 0x00;
P6DIR = 0x00;

InitI2C(); // Initialize I2C module


_EINT();

unsigned int i;
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P3SEL |= 0xC0; // P3.6,7 = USART1 option select
BCSCTL1 &= ~XT2OFF; // XT2on

do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for (i = 0xFF; i > 0; i--); // Time for flag to set
}
while ((IFG1 & OFIFG)); // OSCFault flag still set?

BCSCTL2 |= SELM_2 + SELS; // MCLK= SMCLK= XT2 (safe)

for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i,dat1[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i+1024,dat2[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i+2048,dat3[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
while (1);
}

Para los últimos datos de la EEPROM (código NO FUNCIONAL, solo para


TI MSP430

describir el programa, más adelante se colocará el programa para IAR):

//Esta es la segunda parte para escribir a la eeprom, graba todos los datos restantes.
//IMPORTANTE!!!!!!!!!!!!!!! BORRAR EL CONTENIDO DEL MSP 1612 o de lo contrario este
//Volvera a escribir la eeprom, puede producir errores si no se hace.
P á g i n a | 160

#include "msp430x16x.h"

/*--- external functions of file "I2Croutines.c" ----------------------------*/


extern void InitI2C(void);
extern void EEPROM_ByteWrite(unsigned int Address,unsigned char Data);
extern unsigned char EEPROM_RandomRead(unsigned int Address);
extern unsigned char EEPROM_CurrentAddressRead(void);
extern void EEPROM_AckPolling(void);
/*---------------------------------------------------------------------------*/

char dat1[1024] = {
8 ,
26 ,
234 ,
112 ,
248 ,
124 ,

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.

55 ,
46 ,
192 ,
224 ,
236 ,
36 ,
0
};
char dat2[1024] = {
245 ,
130 ,
228 ,
52 ,
251 ,
245 ,

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.

1 ,
145 ,
2 ,
133 ,
9 ,
149 ,
9 ,
117
};
char dat3[1031] = {
8 ,
37 ,
TI MSP430

1 ,
21 ,
1 ,
9 ,
1 ,
P á g i n a | 161

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.

0 ,
0 ,
0 ,
0 ,
0
};

void main(void)
{
P1DIR = 0x00; // termination of unused pins
P2DIR = 0x00;
P3DIR = 0x00;
P4DIR = 0x00;
P5DIR = 0x00;
P6DIR = 0x00;

InitI2C(); // Initialize I2C module


_EINT();

unsigned int i;
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
P3SEL |= 0xC0; // P3.6,7 = USART1 option select
BCSCTL1 &= ~XT2OFF; // XT2on

do
{
IFG1 &= ~OFIFG; // Clear OSCFault flag
for (i = 0xFF; i > 0; i--); // Time for flag to set
}
while ((IFG1 & OFIFG)); // OSCFault flag still set?

BCSCTL2 |= SELM_2 + SELS; // MCLK= SMCLK= XT2 (safe)

for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i+3072,dat1[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i+4096,dat2[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=1030;i++)
{
EEPROM_ByteWrite(i+5120,dat3[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=10232;i++)
TI MSP430

{
EEPROM_ByteWrite(i+6151,255);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
while (1);
P á g i n a | 162

Ambos códigos a simple vista parecen muy sencillos, ya que hacen uso de
sentencias como:

En apariencia, la función EEPROM_ByteWrite toma la dirección y el dato a


grabar en la EEPROM, mientras que EEPROM_AckPolling se encarga de esperar
a que la operación sea completada.

dat1, dat2… etc. Fungen como arreglos unidimensionales de 1024 bytes, al


momento de seleccionar, por ejemplo dat1[100] seleccionamos el valor 100 de
los 1024 en el arreglo unidimensional.

En la primera sección de ambos programas resaltan algunos comandos


nuevos como lo son extern, esta instrucción llama a una función externa
creada en otro archivo .c, para ser exactos en I2Croutines.c, para agregar de
archivos .c basta con seleccionar en el panel izquierdo Add Files como se
puede ver en la figura:
TI MSP430
P á g i n a | 163

Los programas anteriores son muy sencillos en apariencia, sin embargo el


código que se encarga de hacer funcionar los dos programas anteriores es
este:

/******************************************************************************/
#include "msp430x16x.h"

#define SlaveAddress 0x50;

int PtrTransmit;
unsigned char I2CBuffer[3];

/*---------------------------------------------------------------------------*/
void InitI2C(void)
// Description:
// Initialization of the I2C Module
{
P3SEL = 0x0A; // select module function for the used I2C pins
P3DIR &= ~0x0A;
TI MSP430

// Recommended initialisation steps of I2C module as shown in User Guide:


U0CTL |= I2C+SYNC; // (1) Select I2C mode with SWRST=1
U0CTL &= ~I2CEN; // (2) disable the I2C module
// (3) Configure the I2C module with I2CEN=0 :
P á g i n a | 164

// U0CTL default settings:


// 7-bit addressing, no DMA, no feedback
I2CTCTL = I2CTRX+I2CSSEL_2; // byte mode, repeat mode, clock source = SMCLK,
// transmit mode
I2CSA = SlaveAddress; // define Slave Address
// In this case the Slave Address defines the
// control byte that is sent to the EEPROM.
I2COA = 0x01A5; // own address.
I2CPSC = 0x00; // I2C clock = clock source/1
I2CSCLH = 0x03; // SCL high period = 5*I2C clock
I2CSCLL = 0x03; // SCL low period = 5*I2C clock
U0CTL |= I2CEN; // (4) set I2CEN via software
}

/*---------------------------------------------------------------------------*/
void I2CWriteInit(void)
// Description:
// Initialization of the I2C Module for Write operation.
{
U0CTL |= MST; // define Master Mode
I2CTCTL |= I2CTRX; // I2CTRX=1 => Transmit Mode (R/W bit = 0)
I2CIFG &= ~TXRDYIFG;
I2CIE = TXRDYIE; // enable Transmit ready interrupt
}

/*---------------------------------------------------------------------------*/
void I2CReadInit(void)
// Description:
// Initialization of the I2C Module for Read operation.
{
I2CTCTL &= ~I2CTRX; // I2CTRX=0 => Receive Mode (R/W bit = 1)
I2CIE = RXRDYIE; // enable Receive ready interrupt
}

/*---------------------------------------------------------------------------*/
void EEPROM_ByteWrite(unsigned int Address, unsigned char Data)
// Description:
// Byte Write Operation. The communication via the I2C bus with an EEPROM
// (2465) is realized. A data byte is written into a user defined address.
{
unsigned char adr_hi;
unsigned char adr_lo;

while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations

adr_hi = Address >> 8; // calculate high byte


adr_lo = Address & 0xFF; // and low byte of address

I2CBuffer[2] = adr_hi; // store single bytes that have to be sent


I2CBuffer[1] = adr_lo; // in the I2CBuffer.
I2CBuffer[0] = Data;
PtrTransmit = 2; // set I2CBuffer Pointer
TI MSP430

I2CWriteInit();
I2CNDAT = 3; // 1 control byte + 3 bytes should be transmitted
I2CTCTL |= I2CSTT+I2CSTP; // start and stop condition generation
// => I2C communication is started
}
P á g i n a | 165

/*---------------------------------------------------------------------------*/
unsigned char EEPROM_CurrentAddressRead(void)
// Description:
// Current Address Read Operation. Data is read from the EEPROM. The current
// address from the EEPROM is used.
{
while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations
I2CReadInit();
U0CTL |= MST; // define Master Mode
I2CNDAT = 1; // 1 byte should be received
I2CIFG &= ~ARDYIFG; // clear Access ready interrupt flag
I2CTCTL |= I2CSTT+I2CSTP; // start receiving and finally generate
// re-start and stop condition
while ((~I2CIFG)&ARDYIFG); // wait untill transmission is finished
return I2CBuffer[0];
}

/*---------------------------------------------------------------------------*/
unsigned char EEPROM_RandomRead(unsigned int Address)
// Description:
// Random Read Operation. Data is read from the EEPROM. The EEPROM
// address is defined with the parameter Address.
{
unsigned char adr_hi;
unsigned char adr_lo;

while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations

adr_hi = Address >> 8; // calculate high byte


adr_lo = Address & 0xFF; // and low byte of address

I2CBuffer[1] = adr_hi; // store single bytes that have to be sent


I2CBuffer[0] = adr_lo; // in the I2CBuffer.
PtrTransmit = 1; // set I2CBuffer Pointer

I2CWriteInit();
I2CNDAT = 2; // 1 control byte + 2 bytes should be transmitted
I2CIFG &= ~ARDYIFG; // clear Access ready interrupt flag
I2CTCTL |= I2CSTT; // start condition generation
// => I2C communication is started
while ((~I2CIFG)&ARDYIFG); // wait untill transmission is finished
I2CReadInit();
I2CNDAT = 1; // 1 byte should be received

I2CIFG &= ~ARDYIFG; // clear Access ready interrupt flag


I2CTCTL |= I2CSTT+I2CSTP; // start receiving and finally generate
// re-start and stop condition
while ((~I2CIFG)&ARDYIFG); // wait untill transmission is finished
return I2CBuffer[0];
}

/*---------------------------------------------------------------------------*/
void EEPROM_AckPolling(void)
TI MSP430

// Description:
// Acknowledge Polling. The EEPROM will not acknowledge if a write cycle is
// in progress. It can be used to determine when a write cycle is completed.
{ unsigned int count;
while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations
P5OUT ^= 0x10;
P á g i n a | 166

count=0;
U0CTL &= ~I2CEN; // clear I2CEN bit => necessary to re-configure I2C module
I2CTCTL |= I2CRM; // transmission is software controlled
U0CTL |= I2CEN; // enable I2C module
I2CIFG = NACKIFG; // set NACKIFG
while (NACKIFG & I2CIFG)
{
I2CIFG=0x00; // clear I2C interrupt flags
U0CTL |= MST; // define Master Mode
I2CTCTL |= I2CTRX; // I2CTRX=1 => Transmit Mode (R/W bit = 0)
I2CTCTL |= I2CSTT; // start condition is generated
while (I2CTCTL&I2CSTT); // wait till I2CSTT bit was cleared
I2CTCTL |= I2CSTP; // stop condition is generated after slave address was sent
// => I2C communication is started
while (I2CDCTL&I2CBUSY); // wait till stop bit is reset
count=count+1;
P5OUT ^= 0x10;
}
U0CTL &= ~I2CEN; // clear I2CEN bit => necessary to re-configure I2C module
I2CTCTL &= ~I2CRM; // transmission is by the I2C module
U0CTL |= I2CEN; // enable I2C module

return;
}

/*---------------------------------------------------------------------------*/
/* Interrupt Service Routines */
/* Note that the Compiler version is checked in the following code and */
/* depending of the Compiler Version the correct Interrupt Service */
/* Routine definition is used. */
#if __VER__ < 200
interrupt [USART0TX_VECTOR] void ISR_I2C(void)
#else
#pragma vector=USART0TX_VECTOR
__interrupt void ISR_I2C(void)
#endif

// Description:
// Byte Write Operation. The communication via the I2C bus with an EEPROM
{
switch (I2CIV)
{ case I2CIV_AL: /* I2C interrupt vector: Arbitration lost (ALIFG) */
break;
case I2CIV_NACK: /* I2C interrupt vector: No acknowledge (NACKIFG) */
break;
case I2CIV_OA: /* I2C interrupt vector: Own address (OAIFG) */
break;
case I2CIV_ARDY: /* I2C interrupt vector: Access ready (ARDYIFG) */
break;
case I2CIV_RXRDY: /* I2C interrupt vector: Receive ready (RXRDYIFG) */
I2CBuffer[0]=I2CDRB; // store received data in buffer
break;
case I2CIV_TXRDY: /* I2C interrupt vector: Transmit ready (TXRDYIFG) */
TI MSP430

I2CDRB = I2CBuffer[PtrTransmit];
PtrTransmit = PtrTransmit-1;
if (PtrTransmit<0)
{
I2CIE &= ~TXRDYIE; // disable interrupts
}
P á g i n a | 167

break;
case I2CIV_GC: /* I2C interrupt vector: General call (GCIFG) */
break;
case I2CIV_STT: /* I2C interrupt vector: Start condition (STTIFG) */
break;
}
}

Programa completo para grabar la EEPROM aquí.

Finalmente a la parte que nos interesa, comenzar a escribir la EEPROM del


programador Azul. Abriendo el programa justo unas líneas arriba obtendremos
la siguiente ventana en el IAR:

Es conveniente asegurarnos que estamos en la pestaña WRITE y mirando el


archivo W_EEPROM_01.c, ya con el programador por puerto paralelo puesto y
el programador azul conectado al mismo (y alimentado por USB)
presionaremos Ctrl+D que nos llevará a la siguiente ventana:
TI MSP430
P á g i n a | 168

Entonces procederemos a bajar el cursor hasta llegar a la instrucción while(1):


y presionaremos :
TI MSP430

El programa tardará entonces unos minutos, es preciso entonces permitir que


el programa termine y una imagen similar a la siguiente se muestre:
P á g i n a | 169

Terminado esto, hay que salir del modo debug con el botón y repetir el
mismo proceso pero ahora con la pestaña y el programa:
TI MSP430
P á g i n a | 170

Para terminar nos colocamos en DUMMY y en el programa main.c:


TI MSP430
P á g i n a | 171

Y presionamos Ctrl+D. Esto se hace para que el microcontrolador no vuelva a


intentar programar de nueva cuenta la EEPROM. Cerramos el modo debug y
después el IAR, nuestra EEPROM finalmente está programada, sin embargo no
es el proceso final para convertir el programador azul en rojo, ya que si lo
colocamos tal cual está al USB este simplemente no será reconocido como tal.

El último paso que falta es usar el programa MSP430 FLASH PROGRAMMING


UTILITY y reprogramar el MSP430F1612 contenido en el programador azul. Este
programa tiene la peculiaridad de que acepta archivos .hex para grabar los
TI MSP430

microcontroladores, además de otras funciones interesantes como lo son la


lectura del programa contenido en algún microcontrolador MSP (de esta
manera obtuvimos el código contenido en un ez430-RF2500).
P á g i n a | 172

Entonces, abriendo el programa observaremos una ventana así:

Y nos aseguraremos de que esté configurado correctamente para usar nuestro


programador por puerto paralelo:
TI MSP430
P á g i n a | 173

Presionaremos OK y abriremos el archivo realrojo.bin:


TI MSP430
P á g i n a | 174

Y listo, todo lo que queda es presionar AUTO y el programa hará el resto:


TI MSP430
P á g i n a | 175

Cuando el programa termine, se puede desconectar todo y probar el


programador, si este es reconocido como tal preguntará por los drivers igual
que la primera vez que se instaló, recomiendo seguir los mismos pasos para
encontrar los drivers dentro de la carpeta IAR.

La prueba para verificar si se tiene RS232 o no, consiste en puentear TX y RX y,


abriendo el Hyperterminal escribir cualquier carácter, si este es desplegado en
pantalla el cambio de programador rojo a azul ha sido un éxito.

Con esto se termina la práctica.

Practica 18: El protocolo SPI.


Otro de los protocolos más importantes por su simplicidad es el protocolo SPI 35,
usado en innumerables aplicaciones, algunos ejemplos son chips controlados
TI MSP430

vía éste protocolo e inclusive memorias, dentro de las cuales las más famosas

35
http://es.wikipedia.org/wiki/Serial_Peripheral_Interface
P á g i n a | 176

son las memorias SD36 (Secure Digital) usadas en sin fin de dispositivos
electrónicos de consumo.

En experiencia personal puedo decir que es un protocolo muy parecido al


RS232, puesto que tiene la misma configuración mínima agregando solamente
un hilo más, el de CLK que le permite a mismo establecer una comunicación
síncrona entre maestro esclavo:

SCLK= Slave Clock (Reloj para esclavo)


MOSI= Master Output Slave Input (Salida maestro, entrada esclavo)
MISO= Master Input Slave Output (Salida esclavo, entrada maestro)
SS= Slave Select (Selección de esclavo)

SS puede o no ser necesario dependiendo si se tienen varios dispositivos


esclavos conectados a un solo maestro:

Algunas ventajas del protocolo SPI son que lo que se envía no necesita estar
ligado a ser 8 bits únicamente, se pueden enviar cadenas de bits más largas.
Como desventaja, no es un protocolo que esté diseñado para distancias
largas a comparación del RS232.

Para esta práctica se cambio de microcontrolador del MSP430F2132 al


MSP430F2013 (El mismo que viene de serie en el paquete ez430-F2013) sin
embargo la configuración en diferentes familias MSP430 ha probado ser solo el
TI MSP430

cambio de nombre de algunos registros. Leer la hoja de datos en conjunto con

36
http://es.wikipedia.org/wiki/Secure_Digital
P á g i n a | 177

algún ejemplo nos puede dar una idea de cómo portar el código de
microcontrolador en microcontrolador.

La idea de la practica es que un micro MSP430F2013 actuará como maestro


enviando un conteo del 1 al 100 al esclavo, el mismo en cuanto reciba el
numero lo tomará como tiempo de retraso para prender y apagar un LED
localizado en P1.0 y además enviara un carácter “x” indicando que el dato ha
llegado, entonces el microcontrolador maestro prenderá y apagará un LED al
recibir el carácter X.

La conexión de los mismos proporcionada por uno de los ejemplos para el


MSP430F2013:

Nos quedaría algo así usando dos Target Boards del ez430F2013:
TI MSP430
P á g i n a | 178

37

El SDO del maestro viene conectado al SDI del esclavo (Formando un SIMO) y
el SDI del maestro se conecta con SDO del esclavo (formando un MISO).

Por programación a cada microcontrolador que es, ya sea esclavo o maestro.


Sin más que decir he aquí el código (Primero para el maestro):

#include "msp430x20x3.h"

void delay(void) //Función para crear un retraso de tiempo


{
volatile unsigned int i;
volatile unsigned int j;
for (i = 0; i < 1; ++i)
for (j = 12000; j; j--)
;
}

void init_spi(void)
{
P1OUT |= 0x01; //LED
P1DIR |= 0x01; //Salida P1.0

USICTL0 = USISWRST; // Disable SPI during config.


USICTL0 |= USIPE7 + USIPE6 + USIPE5 + USIOE + USIMST;
TI MSP430

//USIPE7 = SDI/SDA port enable


//USIPE6 = Habilitar modo SPI (de lo contrario es modo I2C)
//USIPE5 = SCLK port enable (reloj de sincronización)

37
Admito que… no es lo más estético que he hecho.
P á g i n a | 179

//USIOE = DATA OUTPUT ENABLE


//USIMST = Maestro (de lo contrario es esclavo)
USICTL1 = 0;

USICKCTL = USIDIV_1 + USISSEL_2; // Reloj para el SPI, SMCLK dividido entre 2


USICTL0 &= ~USISWRST; // USI released for operation
}

// Dumb polling method to send a character.


void send_spi(unsigned char c)
{
//Dummy read to clear buffer (slau144c.pdf p. 11-7)
volatile unsigned char null = USISRL;

if (null=='x')
{
//**********************************************Inicio Prende y Apaga P1.0
switch((P1IN&0x01))
{
case 0x01: P1OUT &=~0x01; break;
case 0x00: P1OUT |=0x01; break;
}
//**********************************************Fin Prende y Apaga P1.0}
}

USISRL = c; //Enviamos los datos por SPI...


//Breakpoint aqui para observar que es lo que llega del SPI

USICNT = 8; //primeros 4 bits del registro con un rango de 0 a 16


//actuan como el numero de bits a recibir o enviar
//Break point aqui para observar que es lo que se manda con el SPI
// Wait for the data to be transmitted...
while ((USICTL1 & USIIFG) == 0);

void main(void)
{
unsigned char digit = 1; //Variable de conteo del 1 al 9 para enviar
//por medio de SPI
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

init_spi(); //Inicializacion del modulo SPI del microcontrolador

while(1)
{
digit++;
if (digit > 100) digit = 1;
send_spi(digit); //Enviamos el digito por medio del SPI en nuestra subrutina
delay(); //Llamamos a nuestra función de retraso para observar el
//Cambio
}
}
TI MSP430

El programa por si solo viene algo explicado, sin embargo me permitiré explicar
algunos detalles:
P á g i n a | 180

La línea enmarcada en rojo es un pedazo de código que debe ser colocado


forzosamente, si no se coloca el microcontrolador no admite cambios de
configuración estando habilitado el modo SPI.

Una vez configurado para SPI el micontrolador es importante quitar el bit que
agregamos anteriormente para que este pueda funcionar.

1. Se ha creado una función send_spi que admite un tipo de dato char (un
numero de 0 a 255)
2. Lo primero que se debe de hacer para enviar un dato por SPI es liberar
el Buffer (Se puede ver como un tanque de almacenamiento) para que
este pueda ser usado al momento de enviar datos, esto se logra
TI MSP430

declarando una variable null que es igualada en la misma declaración


a USIRL (nuestro Buffer) provocando el vaciado en automático de este
en la variable null. Esta misma variable la usamos para tomar un dato
enviado del esclavo (el que le dice que la transmisión ha sido exitosa) y
a su vez la comparamos con el carácter “x”, si el dato llegado del
P á g i n a | 181

esclavo coincide con esta comparación entonces prenderá o apagará


el LED indicándonos que la transferencia fue exitosa.
3. El dato que hemos recibido fue colocado en una variable c al inicio de
la declaración de la función, esta variable simplemente la igualamos al
Buffer USIRL y en automático el MSP se encargará de enviar el dato al
esclavo. Es importante mencionar que USICNT está igualado a 8 porque
ese es el tamaño de la palabra que acabamos de enviar (8 bits) que
para un numero del 1 al 100 es más que suficiente, esto se hace cada
vez que se envía un dato al esclavo. Si nosotros colocáramos un valor
16, entonces el micro enviaría 16 bits al esclavo (rellenando aquellos
espacios no ocupados con ceros), modificando otro bit en el registro
USICTL0 se pueden enviar datos de 32 bits, más información en la hoja
de datos38.

El resto del código no es necesario explicarlo. Ahora observemos el código


para el SPI esclavo:

#include <msp430x20x3.h>

unsigned int i;
unsigned char receive;

void main(void)
{
WDTCTL = WDTPW+WDTHOLD;

P1OUT |= 0x01; //LED


P1DIR |= 0x01; //Salida P1.0

USICTL0 = USISWRST; // Disable SPI during config.


USICTL0 |= USIPE7+USIPE6+USIPE5+USIOE;
//USIPE7 = SDI/SDA port enable
//USIPE6 = Habilitar modo SPI (de lo contrario es modo I2C)
//USIPE5 = SCLK port enable (reloj de sincronizacion)
//USIOE = DATA INPUT ENABLE
USICTL1 |= USIIE; //USIIE = USI Counter Interrupt Enable

//USICKCTL = USIDIV_1 + USISSEL_2; No se requiere configuracion de reloj en


//modo esclavo.

USICTL0 &= ~USISWRST;

_BIS_SR(GIE); //Habilitamos Interrupciones

while(1)
{
switch((P1IN&0x01)) //Prendemos y apagamos un LED
{
case 0x01: P1OUT &=~0x01; break;
case 0x00: P1OUT |=0x01; break;
TI MSP430

}
for(i=0;i<=(receive*100);i++); //Retraso de tiempo controlado por el dato
//llegado del modulo SPI

38
PDFs\msp430f2013.pdf
P á g i n a | 182

}
}

// USI interrupt service routine


#pragma vector=USI_VECTOR
__interrupt void universal_serial_interface(void)
{
receive = USISRL; //Obtenemos el dato del registro del modulo SPI
USICNT = 8; //son 8 bits los que extraeremos del mismo

USISRL = 'x'; //inmediatamente mandamos un dato por SPI


USICNT = 8; //al ser un caracter siguen siendo 8 bits...
}

En este programa las cosas a remarcar son:

1. USIMST no se coloca al ser configuración para esclavo.


2. Se requiere una interrupción para saber que el buffer ha sido llenado (y
que por ende se tiene un dato a la espera de ser usado) es por eso que
se habilitan las interrupciones.

En la interrupción simplemente volcamos el contenido del Buffer USISRL en una


variable global receive para que pueda ser usado por la función main más
arriba.
TI MSP430

Video del programa en funcionamiento. 39

39
Ignorar las voces de fondo…
P á g i n a | 183

Programa para maestro y esclavo SPI aquí.

Con esto se da por terminado el manual Nulo-Intermedio para


microcontroladores de la familia MSP430, aunque se adjuntan otros ejemplos
(La explicación de ellos la dejo al lector, se dan varias pistas de ello en los
mismos programas).

Extra 1.

Medidor de frecuencia de 0 a 20 Hz (salida binaria de 12 a 255):


Programa. Video.

Extra 2.

Uso del paquete ez430-RF2500 para monitoreo inalámbrico con LABVIEW


(Recomendable observar toda la documentación dentro de la carpeta
Wireless MSP430).
Programa. Programa para LabVIEW 8.2.
TI MSP430