Sunteți pe pagina 1din 179

Manual de

Programación

MSP430
Texas Intruments
TI MSP430

Presentado por:
Erick Noe Amezquita Lucio
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


TI MSP430

Manejo de puertos en MSP430.___________________________________________65


Practica 3: Uso de entradas y salidas en los puertos.___________________________65
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 I2C.________________________________________________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:

P1DI
R

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:

P1DI 0 0 0 0 0 0 0 0
R

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í:

P1DI 1 1 1 1 1 1 1 1
R

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í:

P1DI 0 0 0 0 1 0 0 0
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

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í:

P1OU
T

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í:

P1OU 0 0 0 0 1 0 0 0
T

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:

P1DI 0 0 0 0 0 0 0 0
R

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

Puerto 1 como solo entrada.

P1OU 0 0 0 0 1 0 0 0
T

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…:

P1DI 0 0 0 0 1 0 0 0
R

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

Puerto 1 como solo entrada.

P1OU 0 0 0 0 0 1 0 0
T

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 nada 4, 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:

int x;
TI MSP430

void main(void)

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

{
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.

Ejemplo 2:
P á g i n a | 15

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
//a 127.

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


TI MSP430

un unsigned long long… aunque realmente son números muy grandes, en


mi caso no me he visto en la necesidad de usarlos (además también están
sujetos a la capacidad del micro).

Ejemplo 5:
P á g i n a | 16

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 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.

Como comentario breve pero no menos importante mencionaremos la


estructura de una función:
TI MSP430

tipo_de_dato función(tipo_de_dato variable)


{
//código…
}
P á g i n a | 17

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.

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


TI MSP430

multiplicar lo que reciba… de esta manera si nosotros colocáramos en una


línea de código:
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

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.

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.
TI MSP430

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

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

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á error) pero si lo colocas abajo entonces las cosas cambian:

Ejemplo 5:

int x;

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


{
x=y*2;
}

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

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


queremos colocarla después entonces…
P á g i n a | 20

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.

Ejemplo 1:

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


//Ella contiene todas las rutinas necesarias para
//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
TI MSP430

//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
P á g i n a | 21

//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:

#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
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.
TI MSP430
P á g i n a | 22

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.

Entrad Entrad AND OR XOR


aA aB (Multiplicación (Suma (OR
) ) Especial
Salida C Salida )
C Salida C

0 0 0 0 0

0 1 0 1 1

1 0 0 1 1

1 1 1 1 0

Entrad NOT
aA (Negación)
Salida B

0 1

1 0

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.
TI MSP430

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.
P á g i n a | 23

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
variables y entrada.
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.

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ó División


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.
TI MSP430

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

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

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 Diferent


a que igual que igual que e
que de

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:

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?

Pues bien… aquí entran los corrimientos.

Corrimient Corrimient
o a la o a la
derecha izquierda

Símbol >> <<


o

Ejempl x>>8; x<<8;


o

Corremos Corremos
8 espacios 8 espacios
a la a la
TI MSP430

derecha un izquierda
registro un registro
P á g i n a | 25

¿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:

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

21 21 21 21 21 21 2 2 2 2 2 2 2 2 2 2
5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0

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:
TI MSP430

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

21 21 21 21 21 21 2 2 2 2 2 2 2 2 2 2
5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
P á g i n a | 26

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

0xF 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
F

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

= 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:

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 2 2


1 0

>> Corrimiento 8 bits a la derecha


8

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

Hemos corrido los bits más altos. 21 21 21 21 21 21 2 2


5 4 3 2 1 0 9 8
TI MSP430

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


P á g i n a | 27

0xF 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
F

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

Notación real ya con los bits 27 26 25 24 23 22 2 2


1 0
corridos

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
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.

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
TI MSP430

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.
P á g i n a | 28

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];

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.

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:
TI MSP430

void main (void)


{
while(1)
P á g i n a | 29

{
//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 infinito. Es importante tener en cuenta que se debe de evitar el uso de
muchos 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)
{
TI MSP430

while(1)
{
x=x+1; //incrementa el valor de x en 1… también pude haber puesto ‘x++;’

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

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
{ //mucho las condiciones descritas más arriba.
break; //Salir del ciclo infinito
}
Else //De cualquier manera
{
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
TI MSP430

que es uno de los comandos más útiles.

Sentencia Switch y Case


P á g i n a | 31

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;
break;
case 5:
action = 5;
break;
case 6:
action = 6;
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).
TI MSP430

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.
P á g i n a | 32

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…


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)
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


TI MSP430

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

Sentencia while
P á g i n a | 33

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.
}
}

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:

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:
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 | 34

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

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


while

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
TI MSP430

par.
P á g i n a | 35

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 para lo que queremos hacer) y, la versión de evaluación, que
permite usar el compilador sin restricción alguna… por 30 días 13.

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:

O0PLK9HW4DPFBVT5X64Z4DN2FEVYDNR5RRZPRRHJEV9W81EKWKESWXYW
KW6MNSVQN3ZEQ7SM7JTZ5GPFDI7JCAPK2KBWI5N90OODJOHI3ZE0UBCLVAA
0F0IOB2SM2002HESUSC0YHYU0HSKI0P5RHVRCTTJVD1LMOARVSF3QZT0LCD
MAMHP2VSIALJ4C# Feature: EW430-KS4 Version: 01_WIN Temporary license
(Licno:9540-844-764-6148), expires 2033-07-23

Una vez instalado tendremos algo como esto:


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 | 36

Es entonces tiempo de proceder a crear nuestros programas.

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


TI MSP430

aparecerá un cuadro de dialogo pidiéndoles donde quieren guardar su


recién creado proyecto, pueden ponerle el nombre que más les convenga:
P á g i n a | 37

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:
TI MSP430
P á g i n a | 38

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:

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
P á g i n a | 39

Eso en el caso del microcontrolador MSP430F2132, si tienes otro


microcontrolador lo recomendable es fijarse en el directorio C:\Archivos de
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
TI MSP430

en el mismo programa y lo simulará desde ahí. Finalmente presiona OK.


P á g i n a | 40

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 | 41

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 | 42

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).
TI MSP430

14
Es posible que Windows XP, Vista (32 Bits) les muestre advertencias de que el hardware no ha sido
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 | 43

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 | 44

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


Dispositivos de interface humana:

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.
TI MSP430

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

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.

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


#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.
TI MSP430

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.
}
P á g i n a | 46

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:

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 | 47

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


herramientas más poderosas que haya probado 17 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 | 48

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 | 49

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
instrucción y no solo eso, además IAR te permite ver que está pasando en
TI MSP430

los registros del microcontrolador como lo muestran las siguientes capturas:


TI MSP430 P á g i n a | 50
P á g i n a | 51

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 a crear otro, esta vez alternaremos el encendido y apagado
TI MSP430

del LED…
P á g i n a | 52

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 | 53

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 | 54

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 | 55

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 apreciaran cambios. Por ejemplo. Si nosotros
estamos trabajando en maintest2.c y tenemos seleccionada la pestaña
P á g i n a | 56

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 | 57

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”

unsigned int delay (unsigned int x)


P á g i n a | 58

{
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 osciloscopio 2.3 KHz. La función delay agrega un conteo el cual el
TI MSP430

microcontrolador hace internamente, consumiendo recursos y por ende


retrasando el encendido y apagado del LED.

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
P á g i n a | 59

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 | 60

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 | 61

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 | 62

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:

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


TI MSP430

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

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

la del 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 | 64

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 | 65

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)
{
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
TI MSP430

//es de 1 en 1. (15 en decimal, 1111 en binario)


{
P1OUT=i; //A la salida colocamos el conteo.
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.
P á g i n a | 66

//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-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í:

0 0 0 1 0 0 0 1
TI MSP430

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


1 2 3 4 (0x08) (0x04) (0x02) (0x01)
(0x80) (0x40) (0x20) (0x10)
P á g i n a | 67

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 2 3 4 (0x08) (0x04) (0x02) (0x01)
(0x80) (0x40) (0x20) (0x10)

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 4
(0x20) (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.


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.
TI MSP430

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

#include "msp430x21x2.h"

int main( void )


P á g i n a | 68

{
// 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

3 4

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


P á g i n a | 69

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 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
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.
TI MSP430

P1OUT&=~0x01
P á g i n a | 70

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


P1OUT&=~0x… cuando nosotros lo deseemos, puede ser terminando un
ciclo o realizando alguna operación.

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.
TI MSP430
P á g i n a | 71

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

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


20
Aquí les dejo el PDF del MSP430F2132, la configuración de pines se encuentra ahí.

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

//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);
// 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; //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:


TI MSP430
P á g i n a | 73

1 2

3 4

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
TI MSP430

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.
P á g i n a | 74

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 microcontroladorError: Reference
source not found:

Mucho menos los bits, que vienen a ser explicados justo después del
registro:
TI MSP430
P á g i n a | 75

Lo que significa, que con la hoja de datos nos podemos dar idea de la
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).

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:
TI MSP430
P á g i n a | 76

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 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.

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


configuración del registro:
TI MSP430
P á g i n a | 77

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 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)
TI MSP430
P á g i n a | 78

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)

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
TI MSP430

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 constantes para el caso que mencioné anteriormente) y VR-
= Vss (0v referencia interna)
P á g i n a | 79

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 este punto nuestro ADC se encuentra ya funcionando.

Después nos adentramos en esta instrucción:


TI MSP430

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

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

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
cualquiera, para este caso se declaró una variable resultado:

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
TI MSP430

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,
P á g i n a | 81

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 GuideError:
Reference source not found. 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

Y aquí el programa:

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


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 | 82

//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
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
}
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
TI MSP430

}
else
{
P1OUT=P1OUT&0xF0; //Si de lo contrario
//estan prendidos o apagados los dos
} //Manda un cero a la salida.
P á g i n a | 83

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:

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.

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:


TI MSP430

!((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.
P á g i n a | 84

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
//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;

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)
TI MSP430

__bis_SR_register(CPUOFF + GIE);

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


//GIE (General Interrupt Enable) Habilitacion de interrupciones
P á g i n a | 85

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 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
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:
TI MSP430
P á g i n a | 86

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 | 87

Entonces:

Res2 marca que estamos a 28 grados.


TI MSP430

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

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 MSP430F2132Error: Reference source not found. Por ejemplo, si
nosotros quisiéramos seleccionar la función 2 del pin 24 (P1.3):
TI MSP430
P á g i n a | 89

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;

void main(void)
TI MSP430

{
unsigned char d;
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/***********************************************Inicio Configuracion ADC*/
ADC10CTL1 = INCH_0 + ADC10DIV_0;
ADC10CTL0 = SREF_2 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE + ADC10SR;
P á g i n a | 90

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
}
}
}

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


__interrupt void ADC10_ISR(void)
TI MSP430

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

Videos del programa funcionando: 1, 2.


P á g i n a | 91

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 | 92

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 GuideError: Reference source not found (en
la página 406) del microcontrolador que estamos usando:
TI MSP430

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.
P á g i n a | 93

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.

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

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*/
TI MSP430

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


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

TA0CCR0 = 2200; // PWM Period


P á g i n a | 94

/*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


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; // 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.
TI MSP430

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


el número de frecuencia_micro en la formula denotada anteriormente.
P á g i n a | 95

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;

int main( void )


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

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
}
TI MSP430

return 0;
}

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


P á g i n a | 96

__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
}

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:

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 | 97

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
P á g i n a | 98

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
//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
TI MSP430

{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
P á g i n a | 99

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)
/*******************************************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;
}

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:
TI MSP430
P á g i n a | 100

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 y ACLK pueden ser controlados a la misma frecuencia de MCLK,
divisiones de frecuencia del MCLK o relojes externos al microcontrolador.

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.
TI MSP430
P á g i n a | 101

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).

Y finalmente el archivo de la practica… aquí.

Con esto se concluye el manejo de los timers para el uso de PWM.

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
TI MSP430

cambia el estado del pin P1.1.

El código del programa es este:

#include "msp430x21x2.h"
P á g i n a | 102

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

while(1)
{
for(i=0;i<=10000;i++);
switch((P1IN&0x04))
{
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;
}
}
TI MSP430

//Timer1_A0 interrupt service routine


#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer_A1(void)
{
switch((P1IN&0x02))
P á g i n a | 103

{
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 | 104

El programa aquí.

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"
TI MSP430

unsigned int d;

24
¿Bonito osciloscopio… no?

25
http://en.wikipedia.org/wiki/Rotary_encoder
P á g i n a | 105

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)

__bis_SR_register(GIE); // Interrupt enable

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:
TI MSP430
P á g i n a | 106

Y esa fuente de reloj externa viene dada por nuestro motor, el cual tiene
acoplado un encoder que produce una señal cuadrada:

Y cosas a remarcar del programa:


TI MSP430
P á g i n a | 107

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 hacer algo que nosotros queremos.

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:
TI MSP430

26
http://es.wikipedia.org/wiki/Triac
P á g i n a | 108

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.

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 | 109

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 programa 27:

//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
{
TI MSP430

IFG1 &= ~OFIFG; // Clear OSC fault flag


i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
27
Su equivalente exactamente en el PIC18F4450 de microchip.
P á g i n a | 110

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;
__bis_SR_register(GIE); // Enter LPM4 w/interrupt
while(1)
{
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...
{
TI MSP430

for(z=0;z<=(res/8);z++);
P1OUT |= 0x01;
for(z=0;z<=2;z++);
P1OUT &= ~0x01;
P2IFG &= ~0x04; // P2.2 IFG cleared
}
P á g i n a | 111

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:

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 | 112

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.
TI MSP430

Y Finalmente para el código de la interrupción.


P á g i n a | 113

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
(jugando con los bits del registro P2IFG se puede lograr esto).

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 | 114

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):


TI MSP430
P á g i n a | 115

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 | 116

En nombre se puede poner lo que sea, damos OK y obtendremos una


ventana como esta:
TI MSP430

En ella seleccionaremos el puerto COM que corresponde a nuestro


programador (se puede ver en el Administrador de Dispositivos de Windows)
P á g i n a | 117

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 | 118

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
}
TI MSP430

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


P á g i n a | 119

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

__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 | 120

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 probado diversas velocidades y los resultados con la modulación 1 son
los esperados).
TI MSP430

UCA0CTL1 se usa para inicializar el modulo RS232 quitando el bit


UCSWRST.
P á g i n a | 121

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 el cual una vez se le es cargado el dato este es enviado rápidamente
TI MSP430

por el pin TX del microcontrolador)

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
P á g i n a | 122

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 que se muestre como ‘1’ cuando esperamos comparar un 1
proveniente del teclado, otro ejemplo es para una a, en la cual la
TI MSP430

tendríamos que escribir también entre apostrofes de tal manera que


aparezca como ‘a’.

29
http://es.wikipedia.org/wiki/ASCII para más información.
P á g i n a | 123

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.

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:
TI MSP430

//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"
P á g i n a | 124

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

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
TI MSP430

//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),
P á g i n a | 125

// 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;
case 0x00: P1OUT |=0x01; break;
}
//**********************************************Fin Prende y Apaga P1.0
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?

}
}
TI MSP430

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


//la conversión.
__interrupt void ADC10_ISR(void)
{
res=ADC10MEM;
P á g i n a | 126

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 | 127

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 por la división de res2 entre 100 (un numero que representa las
centenas de res2), lo mismo con las unidades y decenas; convirtiendo
efectivamente el numero en particular en un carácter que se puede usar en
RS23230.

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.
TI MSP430

#include "msp430x21x2.h"
char rx,i,d,c=0;
char t[]={"\r\nTemp= XXXX C"};

30
Y pensar que se usaba printf(“Temp= %d”,res2); en un PIC…
P á g i n a | 128

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

//*********************************************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 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
{
TI MSP430

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
P á g i n a | 129

//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))
{
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?

}
}

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


TI MSP430

//la conversión.
__interrupt void ADC10_ISR(void)
{
res=ADC10MEM;
ADC10CTL0 &= ~ENC; // Apagamos el ADC
P á g i n a | 130

//***********************************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
recibidos por el programa. Para el manejo de los datos en el mismo
abriremos la ventana principal de LabVIEW:
TI MSP430
P á g i n a | 131

Seleccionaremos la opción remarcada en rojo, Find Examples… y nos


mostrará una pantalla como esta:

Damos clic en la pestaña search y nos mostrara otra pantalla similar a esta:
TI MSP430

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
P á g i n a | 132

Advanced Serial Write and Read.vi. Esto nos llevará a otra ventana, ya
con el ejemplo abierto:

Configura el programa de esta manera:


TI MSP430
P á g i n a | 133

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 | 134

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 | 135

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 | 136

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 | 137

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 | 138

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 | 139

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 | 140

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 | 141

Entonces colocamos un Waveform Chart:

Y un indicador parecido a un termómetro:


TI MSP430
P á g i n a | 142

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 | 143

Acomodamos y conectamos los dos indicadores extras que justo acabamos


de colocar:
TI MSP430
P á g i n a | 144

Y para terminar, volvemos a ejecutar el programa:


TI MSP430
P á g i n a | 145

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 | 146

Entonces nos colocamos en la pestaña scale y seleccionamos el eje y:


TI MSP430
P á g i n a | 147

Deseleccionamos la opción Autoscale y escribimos la escala que nosotros


deseemos mostrar:
TI MSP430
P á g i n a | 148

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 | 149

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.

Practica 17: El protocolo I2C.


TI MSP430

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 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)
P á g i n a | 150

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):

 Un programador JTAG que pueda ser usado por el programa MSP430


FLASH PROGRAMMING UTILITY33:
TI MSP430

31
http://es.wikipedia.org/wiki/I%C2%B2C

32
http://travisgoodspeed.blogspot.com/2008/05/repurposing-ti-ez430u-part-1.html

33
http://kurt.on.ufanet.ru/
P á g i n a | 151

 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
P á g i n a | 152

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 datos 34 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 | 153

Lo cual nos vuelve a dar una idea de que con el microcontrolador


TI MSP430

MSP430F1612 es posible reprogramar la memoria EEPROM con el código del


programador rojo.

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
P á g i n a | 154

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 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).
TI MSP430

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):

//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
P á g i n a | 155

//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] = {
120 ,
78 ,
166 ,
24 ,
TI MSP430

8 ,
166 ,

//Los siguientes datos fueron truncados para poder colocar el código


//el programa se colocará al final de la práctica.
P á g i n a | 156

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


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
TI MSP430

//Volvera a escribir la eeprom, puede producir errores si no se hace.


#include "msp430x16x.h"

/*--- external functions of file "I2Croutines.c" ----------------------------*/


extern void InitI2C(void);
extern void EEPROM_ByteWrite(unsigned int Address,unsigned char Data);
P á g i n a | 157

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 ,
1 ,
21 ,
1 ,
9 ,
1 ,

//Los siguientes datos fueron truncados para poder colocar el código


TI MSP430

//el programa se colocará al final de la práctica.

0 ,
0 ,
0 ,
P á g i n a | 158

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++)
{
EEPROM_ByteWrite(i+6151,255);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
while (1);
}
TI MSP430

Ambos códigos a simple vista parecen muy sencillos, ya que hacen uso de
sentencias como:
P á g i n a | 159

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 | 160

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 :
// U0CTL default settings:
P á g i n a | 161

// 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

I2CWriteInit();
I2CNDAT = 3; // 1 control byte + 3 bytes should be transmitted
TI MSP430

I2CTCTL |= I2CSTT+I2CSTP; // start and stop condition generation


// => I2C communication is started
}

/*---------------------------------------------------------------------------*/
unsigned char EEPROM_CurrentAddressRead(void)
P á g i n a | 162

// 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)
// 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.
TI MSP430

{ unsigned int count;


while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations
P5OUT ^= 0x10;
count=0;
U0CTL &= ~I2CEN; // clear I2CEN bit => necessary to re-configure I2C module
I2CTCTL |= I2CRM; // transmission is software controlled
P á g i n a | 163

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) */
I2CDRB = I2CBuffer[PtrTransmit];
PtrTransmit = PtrTransmit-1;
if (PtrTransmit<0)
{
TI MSP430

I2CIE &= ~TXRDYIE; // disable interrupts


}
break;
case I2CIV_GC: /* I2C interrupt vector: General call (GCIFG) */
break;
case I2CIV_STT: /* I2C interrupt vector: Start condition (STTIFG) */
P á g i n a | 164

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 | 165

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 | 166

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 | 167

Para terminar nos colocamos en DUMMY y en el programa main.c:


TI MSP430
P á g i n a | 168

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
TI MSP430

archivos .hex para grabar los 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 | 169

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 | 170

Presionaremos OK y abriremos el archivo realrojo.bin:


TI MSP430
P á g i n a | 171

Y listo, todo lo que queda es presionar AUTO y el programa hará el resto:


TI MSP430
P á g i n a | 172

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
SPI35, usado en innumerables aplicaciones, algunos ejemplos son chips
TI MSP430

controlados vía éste protocolo e inclusive memorias, dentro de las cuales las

35
http://es.wikipedia.org/wiki/Serial_Peripheral_Interface
P á g i n a | 173

más famosas son las memorias SD 36 (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 cambio de nombre de algunos registros. Leer la hoja de datos en conjunto
con algún ejemplo nos puede dar una idea de cómo portar el código de
TI MSP430

microcontrolador en microcontrolador.

36
http://es.wikipedia.org/wiki/Secure_Digital
P á g i n a | 174

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 | 175

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.


TI MSP430

USICTL0 |= USIPE7 + USIPE6 + USIPE5 + USIOE + USIMST;


//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 | 176

//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 | 177

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 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
TI MSP430

“x”, si el dato llegado del 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
P á g i n a | 178

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;
}
for(i=0;i<=(receive*100);i++); //Retraso de tiempo controlado por el dato
//llegado del modulo SPI
}
}

// USI interrupt service routine


TI MSP430

#pragma vector=USI_VECTOR
__interrupt void universal_serial_interface(void)
{
receive = USISRL; //Obtenemos el dato del registro del modulo SPI
38
PDFs\msp430f2013.pdf
P á g i n a | 179

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.

Video del programa en funcionamiento.39


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).
TI MSP430

Extra 1.

39
Ignorar las voces de fondo…
P á g i n a | 180

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

S-ar putea să vă placă și