Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programación
MSP430
Texas Intruments
Presentado por:
Erick Noe Amezquita Lucio
TI MSP430
Acerca de este manual:
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
Compilador: ______________________________________________________________ 7
Sistema Binario:____________________________________________________________ 7
Registros: ___________________________________________________________ 8
Lenguaje C:________________________________________________________ 12
Variables y tipos de dato _________________________________________________ 13
Funciones ________________________________________________________________ 16
µ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.
1.- Necesitamos un puerto, puede ser el que sea, en este caso usaremos el
puerto 1 del µC MSP430F2274
Pin 3 del µC
Página |9
3.- Bien, ya sabemos que pin queremos que encienda el LED… ¿Qué hacer?
Pues primero hay que decirle al puerto 1 que el pin 3 va a ser un pin de salida
(es decir, que de el se obtendrán señales 0 y 1 lógicos dependiendo de cómo
lo usemos, su contraparte es que reciba señales de 0 y 1 lógicos)1
Para esto, haremos que hemos leído la hoja de datos (Users Guide de este
micro), y que sabemos que PxDIR es un registro de 8 bits que controla si son
entradas o salidas los pines de un puerto x.
Quiere decir que P1DIR controla los pines del puerto 1 como entradas o salidas.
Para que se den una idea más clara, he aquí una tabla:
P1DIR
Representación del registro P1DIR2. ¿Notas los 8 espacios? Por eso es un registro
de 8 bits…
P1DIR 0 0 0 0 0 0 0 0
P1DIR 1 1 1 1 1 1 1 1
Y, si solo quisiéramos como dijimos arriba el pin P1.3 como salida y todos los
demás de entrada… entonces este tiene que quedar así:
TI MSP430
1
Un cero lógico para este micro son 0 volts y un 1 lógico son 3 volts.
2
Para los que usamos PICs esto es un set_tris_a() … considerando a como el puerto 1 y cambiando la
notación, 1 para salidas y 0 para entradas.
P á g i n a | 10
P1DIR 0 0 0 0 1 0 0 0
Pin P1.3 como solo salida y los demás como solo entrada.
4.- ¡Bien! Tenemos un bonito P1.3 como salida… pero por si solo no va a hacer
nada, hay que decirle que por ese pin “saque” un 1 lógico… y para eso, existe
un registro que de manera análoga se encarga de hacer eso, para el puerto 1
este sería así:
P1OUT
P1OUT 0 0 0 0 1 0 0 0
¡Sas! Se tiene un 1 lógico a la salida del pin P3.1 listo para prender un LED.
P1DIR 0 0 0 0 0 0 0 0
P1OUT 0 0 0 0 1 0 0 0
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
P1DIR 0 0 0 0 1 0 0 0
P1OUT 0 0 0 0 0 1 0 0
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).
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.
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.
Ejemplo 1:
Ejemplo 2:
TI MSP430
int x;
4
¡Desu!
P á g i n a | 13
void main(void)
{
while(1)
/*
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).
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
Ejemplo 1:
Ejemplo 2:
Ejemplo 3:
x=100-200;
Ejemplo 4:
delay(15);
a=120; //almacenamos ahora un numero, en los char puede ser un numero //en el
intervalo de 0 a 255. Para los signed char el intervalo es de -128
TI MSP430
//a 127.
Ejemplo 5:
float z=0.8;
z=34.56;
Ejemplo 6:
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.
Se dice que una variable es local cuando esta dentro de una función o una
subrutina, no las puedes llamar cuando tú quieras si no exclusivamente dentro
de esa función o esa subrutina (o consecuentes subrutinas).
En el siguiente punto se verá que es una función y más adelante que es una
subrutina.
2.- Funciones
Ok… este es otro apartado grande, seguramente has visto cosas como ‘void
main(void)’ en algunos de los ejemplos de arriba, esa línea de código
representan una función.
Las funciones son relativamente lo primero que el compilador mira, puesto que
dentro de ellas se inserta todo el código que será ejecutado en el micro, en
TI MSP430
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().
Ejemplo 1:
La función de void es: si está antes de la función significa que esta no entrega
valor alguno de regreso, y si está dentro de los paréntesis (argumento) significa
que tampoco recibe nada.
Normalmente la función main() siempre se usa tal cual la acabo de poner (al
ser la principal nunca se ha requerido ni que reciba ni que entregue algo) así
que para futuros programas puedes hacer un copy & paste del mismo.
TI MSP430
6
Esto no siempre compila en el IAR, depende de que micro uses… pero con un simple ‘#Define True 1’
hasta arriba del código se puede arreglar, para este caso esta más que presente en el compilador.
P á g i n a | 18
Ejemplo 2:
int x; //Variable global para que la función la pueda usar aun estando fuera
//de ella.
En este ejemplo se puede ver una función cuya única operación es multiplicar
lo que reciba… de esta manera si nosotros colocáramos en una línea de
código:
multiplica(2);
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:
Esta función lo único que hace es contar, contar hasta 1000 y repitiendo el
conteo x veces (ese valor se lo damos nosotros, al estilo de delay(100);) eso
inevitablemente consume recursos del micro y genera un retraso…
normalmente se pone entre líneas de código, para que el micro ‘espere’ un
tiempo antes de ejecutar una segunda instrucción.
TI MSP430
7
Sip… en estos micros no hay una función bonita delay() como lo había en los PICs.
P á g i n a | 19
Ejemplo 4:
} //fin de la función.
Bien, he aquí nuestra función que entrega y recibe datos… ¿que hace? Bueno
si tú pones unas líneas de código:
Nota:
Ejemplo 5:
int x;
P á g i n a | 20
void main(void)
{
int j;
while(1)
{
multiplica(20);
j=x;
}
}
Ejemplo 6:
int x;
void main(void)
{
int j;
while(1)
{
multiplica(20);
j=x;
}
Ejemplo 1:
#include “msp430x21x2.h"
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.
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.
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
Entrada NOT
A (Negación)
Salida B
TI MSP430
0 1
1 0
P á g i n a | 23
Creo que de todas quizás la más confusa sea la XOR, pero si lo quieres ver de
una forma fácil, la XOR compara dos entradas y si las dos son iguales a la
salida obtienes un 0, de lo contrario es 1.
Su notación en C(IAR8):
Símbolo & | ~ ^
Los operadores lógicos son muy usados puesto que casi toda nuestra
interacción con el micro es en bits, dígase los puertos por ejemplo, tienen pines
y cada uno de ellos para nosotros es un bit.
TI MSP430
8
En los PICs, que normalmente usamos PIC C Compiler… la notación es algo diferente.
9
Los NOT solo tienen una entrada…
P á g i n a | 24
Operadores Aritméticos:
Estos son los de toda la vida… son muy usados a la hora de convertir resultados
provenientes del ADC por decir un ejemplo.
Símbolo + - * /
Condiciones:
Corrimientos:
TI MSP430
Bien, hemos llegado a una parte que es mas ‘truco’ que otra cosa… los
corrimientos responden a una pregunta que me hice mucho tiempo:
Corrimiento Corrimiento
a la a la
derecha izquierda
Corremos 8 Corremos 8
espacios a espacios a
la derecha la izquierda
un registro un registro
x 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1
y=x&0xFF; //le decimos que multiplique por 1 los primeros 8 bits y que los
//almacene en y. Es decir:
x 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1
0xFF 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
27 26 25 24 23 22 21 20
= 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1
Resultado almacenado en y: 0 0 1 0 0 0 1 1
//y solo puede tomar 8 bits por ser unsigned char. A esto se le dice
//almacenar la parte baja de x en y (los primeros 8 bits). Ahora vamos con la
//parte alta (los últimos 8 bits).
x 0 1 1 0 0 0 1 0 0 0 1 0 0 0 1 1
0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0
Hemos corrido los bits más altos. 215 214 213 212 211 210 29 28
0xFF 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
= 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0
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.
Abreviaciones:
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.
Otro ejemplo:
t[0]=10;
t[1]=100;
t[2]=124;
unsigned char x;
unsigned char y;
unsigned char z;
unsigned char w;
x=a[0];
y=a[1];
z=a[2];
w=a[3];
TI MSP430
Algunos de los programas que se verán en la sección de IAR harán uso de ellos
de una manera muy útil es por eso la mención de los mismos.
P á g i n a | 29
Ciclos infinitos:
Ejemplo 1:
Ejemplo 2:
Ejemplo 3:
for sin argumento alguno en los paréntesis también se comporta como un ciclo
TI MSP430
10
En los PICs era de ley siempre el TRUE.
P á g i n a | 30
ciclos infinitos ya que pueden bloquear al micro (al igual que las PCs también
se bloquean). La única manera de salirse de los ciclos infinitos es con la
instrucción break; que literalmente ‘rompe’ el ciclo.
Condicionales:
Sentencia if
Ejemplo 1:
void main(void)
{
while(1)
{
x=x+1; //incrementa el valor de x en 1… también pude haber puesto ‘x++;’
if(x>=10) //Si x es igual a 5…
{
break;
}
}
}
Ejemplo 2:
void main(void)
{
for(;;) //Ciclo infinito..
{
if(x==10) //Si x es igual a 10…
{
x=15; //Iguala a x con 15…
}
else if(x>=20) //De cualquier manera si x es igual o mayor que 20… en estos if se usan
TI MSP430
x++; //Incrementa x en 1.
}
}
}
Más de una ocasión nos vamos a encontrar con que requerimos colocar
muchos if’s creando un código poco legible, es entonces cuando se usa
switch y case:
Ejemplo 1:
unsigned int c;
unsigned int action;
void main(void)
{
while(1)
{
c++; //incremento en 1 con cada ciclo de programa…
}
switch(c) //uso switch con la variable c por argumento…
{
case 1: //en caso de que c=1 entonces
action = 1; //dale a la variable action un valor de 1.
break;
case 2: //en caso de que c=2 entonces
action = 2; //dale a la variable action un valor de 2… etc.
break;
case 3:
action = 3;
break;
case 4:
action = 4;
TI MSP430
break;
case 5:
action = 5;
break;
case 6:
action = 6;
P á g i n a | 32
break;
default: //Default vendría a ser el else a secas. Si no encuentra una condición
action = 7; //definida para c entonces usa esta, que le da el valor a action de 7.
break;
}
}
}
Como se puede ver cada que la variable c se incrementa esta selecciona una
opción posible de switch. Siendo los cases if’s que nos permiten hacer una
selección diferente. default: se comporta exactamente igual que else, de
manera que si no encuentra una un case que corresponda a alguna opción
que la variable c envíe automáticamente se toma la opción contenida en
default: es importante colocar un break; cuando terminemos de insertar
código en el case: o de lo contrario no marcaremos su fin (y el compilador
dará error).
Ciclos Finitos:
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(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:
void main(void)
{
for(i=3;i<=20;i++) //Esto quiere decir… (Comienza a contar desde 3; mientras sea menor
{ //igual a 20 con incrementos de 1)
P á g i n a | 33
Ejemplo 2:
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)
}
Sentencia while
while significa ‘mientras’ en español, repetirá el ciclo de lo que sea que haga
mientras se cumpla una condición en particular.
Ejemplo 1:
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
void main(void)
{
while( (j>=5) | (j<=10) ) //ahora se cicla de acuerdo a dos condiciones, si j>=5 ó j<=10.
{ //como declaramos con un valor inicial j=5 entonces se cumplirá la condición
j++; //para entrar en el ciclo.
TI MSP430
}
}
En este ejemplo hice uso de varias condiciones a la vez, Se puede poner varias
condiciones de acuerdo a la necesidad que se presente; por ejemplo:
P á g i n a | 34
Las condiciones se pueden usar en los if’s, case’s, y while’s por igual.
Sentencia do + while
Ejemplo 1:
void main(void)
{
while(1)
{
do
{
j++;
}while( !( (j<=5) | (j>=10) ) ); //Condición ‘Mientras j sea diferente de <=5 ó >=10’
x++;
}
}
11
El not como operador es ~, pero para condiciones se usa el !.
12
Esto se traduciría como, ‘si la condición 1 ó la condición 2 y la condición 3 ó la condición 4’ se cumplen
entonces haz algo.
P á g i n a | 35
6 Ciclo do while 5 6
7 Ciclo do while 5 7
8 Ciclo do while 5 8
9 Ciclo do while 5 9
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:
Existen dos versiones gratuitas del IAR Embedded Workbench, la Kickstart que
nos permite hasta 4 Kilobytes de código (muchas veces más que suficiente
TI MSP430
P á g i n a | 36
License Number:
9540-844-764-6148
License Key:
O0PLK9HW4DPFBVT5X64Z4DN2FEVYDNR5RRZPRRHJEV9W81EKWKESWXYWKW6M
NSVQN3ZEQ7SM7JTZ5GPFDI7JCAPK2KBWI5N90OODJOHI3ZE0UBCLVAA0F0IOB2S
M2002HESUSC0YHYU0HSKI0P5RHVRCTTJVD1LMOARVSF3QZT0LCDMAMHP2VSIALJ
4C# Feature: EW430-KS4 Version: 01_WIN Temporary license (Licno:9540-844-
764-6148), expires 2033-07-23
13
Para aquellos que están puestos en este mundo de las PCs, a lo mejor está de más decirlo pero
pueden instalar una maquina virtual, instalar el IAR en su versión de evaluación y de ahí usarlo
indefinidamente reiniciando la maquina virtual cuantas veces sea necesario…
P á g i n a | 37
Hecho esto lo que resta es solamente siguiente, siguiente, siguiente 14. Les
volverá a mostrar de nueva cuenta el asistente de nuevo hardware
encontrado, esto es porque en si el programador es un puerto serial y
programador a la vez (2 en 1, pero el puerto serial solo se puede usar de serie
en el ez430-RF2500 y en el ez430-RFF2013 con modificación previa de la
EEPROM usada por el TUSB3410, se explicara más adelante el proceso para
ello).
14
Es posible que Windows XP, Vista (32 Bits) les muestre advertencias de que el hardware no ha sido
TI MSP430
firmado y si se desea continuar. La respuesta es darle que si o de lo contrario no se instalara el driver del
programador. Además, Aquí estoy usando XP pero puedo corroborar que en Vista (32 Bits) también
funciona (Hay veces que IAR muestra “Parallel tools can’t be used on Windows vista” pero basta con
desconectar y reconectar el programador para que este funcione.)
P á g i n a | 44
Nota importante.15
En algunas pruebas que he realizado he comprobado que en las HP de la serie
DVXXXX el programador NO FUNCIONA (Hablando en especifico, el
programador del paquete ez430-RF2500). ¿Solución? Hay que deshabilitar (y
en un caso desinstalar) el driver del control remoto de HP, lo he comprobado
yo en tres maquinas HP y en efecto ese es el problema (ni idea del por qué se
interfieren el uno al otro)… para ello, posiciónate sobre el icono de Mi PC, clic
derecho del mouse y darle propiedades:
15
Más información en esta página de la Texas Instruments:
https://community.ti.com/forums/t/329.aspx
P á g i n a | 45
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.
16
http://es.wikipedia.org/wiki/Hola_mundo Hello World, recomiendo leerlo para entretenerse un rato.
P á g i n a | 47
#include "msp430x21x2.h"
int main( void ) //Aquí siempre empezaremos nuestro código, como nuestra única
//intención es demostrar el uso del IAR no nos meteremos con
//mucho aun.
{
// Stop watchdog timer to prevent time out reset <- esto siempre lo agregan.
WDTCTL = WDTPW + WDTHOLD; //<- Igual esta instrucción, su función es actuar
//como watchdog, un watchdog resetea el micro
//si no se le da una señal que indique que
//el proceso esta funcionando, muy útil cuando se
//tiene un programa que se traba mucho.
//Son computadoras después de todo y estas también
//Se quedan pasmadas.
P1DIR=0xFF; //<- Aquí le decimos al microcontrolador que todo el puerto 1 será
//de solo salida, como queremos prender un led rápido esta es una
//opción viable siempre y cuando no se quieran conectar entradas a
//este puerto, díganse botones, pushbutons... etc. Noten la
//nomenclatura hexadecimal, es de lo más común en esta programación
//de micros... una forma fácil de ver esto en formato de pines es
//así: F hexadecimal= 15 en decimal=1111 en binario (la calculadora de
//Windows es excelente para esto (modo científica)
//Entonces F F = 1111 1111.
P1OUT=0x01; //La instrucción por si sola se explica, manda un 1 a la salida.
return 0; //El int main al haber sido declarado de esa manera, se recomienda
//colocar esta instrucción para evitar problemas (como se vio
//anteriormente la función busca regresar un valor, así que... que
//mejor que el 0 cuando no se quiere hacer nada.
}
Una vez hecho esto, presiona Ctrl+D este comando indica a IAR que compile
el programa y a su vez, si tienes un programador conectado (y un micro que
programar) que inyecte el código al microcontrolador. Te aparecerá un
cuadro de dialogo como el siguiente:
TI MSP430
P á g i n a | 48
Dale el nombre que quieras (yo le puse test1 de nueva cuenta) y guarda.
Aparecerán diálogos con barras de estado que desaparecen tan rápido
como aparecieron (si hay algún error en el programador o el micro te mostrará
un error indicando que o no existe programador o no puede encontrar al
micro). Si todo sale bien, pasaras a la siguiente pantalla.
TI MSP430
P á g i n a | 49
Recordaras los registros P1OUT y P1DIR… hay una forma fácil de apreciarlos en
este Debugger. Selecciona View en el menú principal y después de eso
selecciona Register…
TI MSP430
17
En los PICs de microchip siempre quise algo así.
P á g i n a | 50
Una vez hecho eso a la derecha te habrá salido otra pequeña sección similar
a esta:
Queremos ver acción, así que en el menú desplegable donde menciona CPU
Registers selecciona P1/2:
TI MSP430
P á g i n a | 51
Hecho esto veras nombres algo conocidos, son los registros de entrada y salida
(del puerto 1 como del puerto 2), expande P1OUT y P1DIR (y… si quieres cierra
la ventana que está a un costado, Disassembly, no la usaremos y así liberamos
espacio para poder expandir la ventana de Registers):
instrucción y no solo eso, además IAR te permite ver que está pasando en los
registros del microcontrolador como lo muestran las siguientes capturas:
P á g i n a | 52
TI MSP430
P á g i n a | 53
La siguiente vez que presiones IAR se saldrá del modo debug dejando la
pantalla como en un principio, esperando a que nuevamente presionemos
, esto es porque no ciclamos indefinidamente el programa, que para este
sencillo ejemplo realmente no es necesario. Como buena costumbre
informática, presiona para guardar todo y después presiona para salir
del modo debugger totalmente.
IAR permite tener varios programas en un solo proyecto, así que procederemos
TI MSP430
Esta vez selecciona Empty project y guarda con un nombre diferente… yo usé
test2. Una vez se haya realizado esto la ventana izquierda donde están los
proyectos lucirá así:
Agregando las pestañas de Overview, test1 y test2. Ahora, donde sea que se
hallan guardado los archivos abriremos esa carpeta y haremos una copia de
main.c, puede ser renombrada a gusto, en mi caso maintest2.c.
TI MSP430
P á g i n a | 56
Y entre cada P1OUT coloca un delay(1000); de tal manera que se vea así:
Ahora, hay una manera de ver más a fondo que está haciendo el programa
en sí. Una herramienta muy útil y poderosa del IAR es que se puede apreciar
que están haciendo las variables, notaras que hay un unsigned int i,j; para
observar su comportamiento podemos usar la ventana Locals.
Para borrar esos valores presiona este botón resetea por completo tanto el
programa como la ejecución en el micro, colocándolo todo a cero. Realizado
esto, selecciona Debug en el menú principal y después Autostep…
TI MSP430
P á g i n a | 62
Selecciona Step Into (Source Level) y deja el valor de 1000 (es el tiempo en
que quieres que automáticamente cambie de instrucción, está en
milisegundos, si quieres que sea más rápido el cambio baja el valor, o si lo
prefieres más lento sube el valor)
TI MSP430
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 .
En el caso de cuando son registros que solo cambian de número, por ejemplo
P1DIR, P2DIR, P3DIR… etc. Estos son englobados en uno solo en la hoja de
datos como PXDIR, aquí una muestra:
18
TI MSP430
18
No quería decirlo pero… el inglés es realmente importante aquí.
P á g i n a | 65
Sin más que decir, enfocaré las siguientes líneas a explicar programas
realizados en el IAR.
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.
Si todo sale bien, el microcontrolador, y los LEDs deberían hacer algo como
esto:
TI MSP430
19
Dejo entonces la hoja de datos “Users Guide” aquí para microcontrolador MSP430F2132:
PDFs\MSP430x2xxx_GRL.pdf
P á g i n a | 66
1 2
3 4
El registro P1IN entonces tendrá un valor de 1010 0000. Ese valor lo guardamos
en x con la instrucción:
TI MSP430
#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;
}
return 0;
}
0 0 0 1 0 0 0 1
0 0 0 1 0 0 0 0
0 0 1 1 0 0 0 1
0 0 1 1 0 0 0 0
Para cerrar con broche de oro el uso de los puertos, tanto de entrada como
de salida se mostrará el uso individual de los pines, en esta ocasión para ser
usados como salidas individuales.
P á g i n a | 70
#include "msp430x21x2.h"
1 2
TI MSP430
P á g i n a | 71
3 4
Explicando el programa:
La parte de los IF se vio anteriormente, aquí lo usamos solo para poder tener
varias entradas individuales.
En el programa se pueden apreciar también varios else, esto nos sirve para
que cuando el dip-switch no sea presionado automáticamente tome la
P á g i n a | 72
~0x01
Aquí realiza un NOT de un byte entero, en este caso 0x01=0000 0001, entonces
~0x01=1111 1110.
P1OUT&=~0x01
Aquí una vez realizada la operación del NOT la operación pasaría a ser
P1OUT&=1111 1110 y esta a su vez pasaría a ser 0000 0011&=1111 1110.
Entonces:
Todas estas instrucciones aplican a todos los puertos, basta con cambiar el 1
por el puerto deseado a utilizar, puede ser P2OUT, P3OUT… etc.
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).
20
Aquí les dejo el PDF del MSP430F2132, la configuración de pines se encuentra ahí.
P á g i n a | 74
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilize.
while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
__bis_SR_register(CPUOFF + GIE);
TI MSP430
21
Y me acordaba del… read_adc();
P á g i n a | 75
return 0;
}
1 2
3 4
TI MSP430
P á g i n a | 76
5 6
Video en funcionamiento.
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.
Mucho menos los bits, que vienen a ser explicados justo después del registro:
Lo que significa, que con la hoja de datos nos podemos dar idea de la
TI MSP430
ADC10DIV_0 viene a ser una división entre 1, es decir, el reloj que se escoja
posteriormente para su funcionamiento no será dividido entre nada y
funcionara a la velocidad del mismo, ADC10DIV_1 configura una división entre
2, si nosotros tenemos un reloj de 8 MHz, la frecuencia a la cual funcionará el
TI MSP430
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.
hasta 1.5 volts (si nosotros intentamos medir algo más arriba de eso en el
resultado del ADC siempre obtendremos 1023, que es el número máximo de los
10 bits de conversión)
P á g i n a | 80
000 vendría a ser SREF_0, referencia positiva = VR+ = Vcc (3v Interna) y
referencia negativa =VR- = Vss (0v Interna) es decir, la misma alimentación del
microcontrolador.
001 es SREF_1 y aquí VR+ = Vref+ (1.5v interna) y VR- = Vss (0v interna)
TI MSP430
002 = SREF_2. Esta configuración es una de las más usadas, si no se desea tener
un límite de hasta 3 volts, si no algo un poco mayor como por ejemplo 5 volts
se requiere forzosamente de una referencia positiva externa. Resumiendo, esta
configuración VR+ = Veref+ = Referencia externa en el pin P2.4 (5 volts
P á g i n a | 81
REFON inicializa el generador de referencia interno del micro, sirve para poder
usar las referencias internas.
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.
22
Aquí explican más a detalle este concepto http://en.wikipedia.org/wiki/Sample_and_hold
P á g i n a | 82
CPUOFF se refiere tal cual a apagar el CPU del microcontrolador, esto para
entrar en modo de bajo consumo de energía y además GIE se encarga de
habilitar las interrupciones, en este caso, la responsable de que el CPU vuelva
a despertar y continúe haciendo operaciones:
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.
Finalmente, para leer el valor proveniente de la conversión ADC solo basta con
volcar el contenido del registro de 10 bits ADC10MEM en una variable
TI MSP430
23
El uso del inglés es… desafortunadamente fundamental para el entendimiento de los mismos. Con lo
mucho que no me gusta decirlo.
P á g i n a | 84
Y aquí el programa:
ADC10CTL1 = ADC10DIV_0;
// ADC0 + clk dividido entre 0 para este clk (sin prescaler)
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilice.
while(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
}
else
{
P1OUT=P1OUT&0xF0; //Si de lo contrario
//estan prendidos o apagados los dos
} //Manda un cero a la salida.
}
return 0;
}
(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.
!((P1IN&0xF0)==0x00)|((P1IN&0xF0)==0x30))
#include "msp430x21x2.h"
for( d = 240; d > 0; d-- ); //Retraso para permitir que la referencia se estabilice.
while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
__bis_SR_register(CPUOFF + GIE);
return 0;
}
Cada grado tiene una resolución de 3.55 mV, y el ADC puede dar como
máximo un numero de 1024… (Por los 1000 se obtenía aproximadamente 135
grados), por si solita la medición de la temperatura contiene un offset, es por
TI MSP430
Entonces:
TI MSP430
Con esta práctica se concluye el uso de los ADCs. La misma la tienes aquí.
P á g i n a | 90
Timers (Temporizadores)
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.
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).
#include "msp430x21x2.h"
volatile unsigned int res;
TI MSP430
void main(void)
{
unsigned char d;
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
/***********************************************Inicio Configuracion ADC*/
P á g i n a | 92
while(1)
{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
__bis_SR_register(GIE); // Habilitar interrupciones
/*******************************************Fin Captura valor ADC*/
if((P1IN&0xF0)==0x10)
{
if((P1OUT&0x03)!=0x01){P1OUT=0xFC; P1OUT|=0x01;}
TA0CCR1 = res; // TA0CCR1 PWM duty cycle
}
if((P1IN&0xF0)==0x20)
{
if((P1OUT&0x03)!=0x02){P1OUT=0xFC; P1OUT|=0x02;}
TA0CCR2 = res; // TA0CCR2 PWM duty cycle
}
if((P1IN&0xF0)==0x30)
{
if((P1OUT&0x03)!=0x02){P1OUT=0xFC; P1OUT|=0x03;}
TA1CCR1 = res; // TA1CCR1 PWM duty cycle
}
if((P1IN&0xF0)==0x00)
{
if((P1OUT&0x03)!=0x00){P1OUT=0xFC;}
TA0CCR1 = 0; // TA0CCR1 PWM duty cycle TA0.1
TA0CCR2 = 0; // TA0CCR2 PWM duty cycle TA0.2
TA1CCR1 = 0; // TA1CCR1 PWM duty cycle TA1.1
}
}
}
TI MSP430
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:
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)
Una manera, un poco más compleja pero más explicativa de cómo opera el
PWM en base a la configuración que le dimos se puede apreciar entiendo la
imagen en el PDF User’s Guide19 (en la página 406) del microcontrolador que
estamos usando:
TI MSP430
P á g i n a | 95
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:
#include "msp430x21x2.h"
volatile unsigned int res;
while(1)
P á g i n a | 97
{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
/*******************************************Fin Captura valor ADC*/
TA0CCR1 = res; // TA0CCR1 PWM duty cycle
}
return 0;
}
#include "msp430x21x2.h"
volatile unsigned int res;
TI MSP430
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;
}
Y a continuación el programa:
//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;
P1OUT&=0xF4;
while(1)
{
/******************************************Captura Valor ADC*/
ADC10CTL0 |= ENC + ADC10SC; // Activacion del ADC + SC (Start Conversion)
TI MSP430
return 0;
}
El programa muestra indicios de ser muy similar a los anteriores excepto por las
siguientes líneas:
Los parámetros que se configuraron para el uso del ADC cambiaron un poco
para hacer más estable al PWM.
#include "msp430x21x2.h"
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
unsigned int i;
//******************************************Inicio configuracion cristal
BCSCTL1 |= XTS + DIVA_3; // Cristal externo + ACLK = (LFXT1 = HF XTAL)/8
BCSCTL3 |= LFXT1S1; // LFXT1S1 = 3-16Mhz
do
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
while (OFIFG & IFG1); // OSC fault flag set?
BCSCTL2 |= SELM_3 + SELS; // MCLK = SMCLK = LFXT1
//********************************************Fin configuracion cristal
P1DIR = 0x0F;
P1SEL = 0x00; // Sin funcion secundaria el puerto 1
P1OUT &= 0x00; // Limpiar puerto 1
while(1)
{
for(i=0;i<=10000;i++);
switch((P1IN&0x04))
P á g i n a | 105
{
case 0x04: P1OUT &=~0x04; break;
case 0x00: P1OUT |=0x04; break;
}
}
}
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.
24
TI MSP430
El programa aquí.
24
¿Bonito osciloscopio… no?
P á g i n a | 107
Código:
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.
25
http://en.wikipedia.org/wiki/Rotary_encoder
P á g i n a | 108
while(1)
{
if(d>=16){d=0;}
P1OUT=d;
//Si quisieramos saber el numero de pulsos que tenemos en TAINCLK
//Podemos usar la variable TA0R para llamar al valor.
//Ejemplo n=TA0R;
}
}
Y esa fuente de reloj externa viene dada por nuestro motor, el cual tiene
acoplado un encoder que produce una señal cuadrada:
TI MSP430
P á g i n a | 109
Esa resistencia puede ser, ya sea un foco, un ventilador… una carga en sí. Para
ello normalmente se recurre a tomar la misma señal senoidal (de CFE por
ejemplo, 120v 60Hz) reduciéndola y creando pulsos cuadrados con la misma.
TI MSP430
26
http://es.wikipedia.org/wiki/Triac
P á g i n a | 111
Ya con la señal cuadrada se recurre a crear pulsos con la misma, los cuales se
desplazan a lo largo del todo el semiciclo:
TI MSP430
P á g i n a | 112
Las interrupciones externas en el msp430F2132 vienen dadas por todos los pines
tanto del puerto 1 como del puerto 2, ellos se pueden controlar fácilmente en
el programa mediante el uso de banderas (bits) en registros que representan
cada uno de los pines. A continuación el programa27:
//Programa para generar pulsos siguiendo una onda patron cuadrada de corriente continúa (NO
//alterna, siempre arriba del eje x), sirve como control para un triac de disparo.
#include "msp430x21x2.h"
unsigned int z,i,prom,res;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR |= 0x0F;
P2DIR &= ~0x06; // P2.1 and P2.2 as input direction
P2IE |= 0x06; // P2.1 and P2.2 interrupt enabled
P2IES &= ~0x02; // P2.2 low to high transition interrupt
P2IES |= 0x04; // P2.2 high to low transition interrupt
P2IFG &= ~0x06; // P2.1 and P2.2 IFG cleared
P1OUT &= 0x00;
TI MSP430
27
Su equivalente exactamente en el PIC18F4450 de microchip.
P á g i n a | 113
/*
//Código innecesario, solo demuestra el uso de interrupciones individualmente
if((P2IFG&0x06)==0x02) // If Interrupt Flag of 2.1 is activated...
{
for(z=0;z<=(res/16);z++);
P1OUT |= 0x01;
for(z=0;z<=2;z++);
P1OUT &= ~0x01;
P2IFG &= ~0x02; // P2.1 IFG cleared
}
else if((P2IFG&0x06)==0x04) // If Interrupt Flag of 2.2 is activated...
{
for(z=0;z<=(res/8);z++);
P1OUT |= 0x01;
for(z=0;z<=2;z++);
P1OUT &= ~0x01;
P2IFG &= ~0x04; // P2.2 IFG cleared
}
else
{
P2IFG &= ~0x02; // P2.1 IFG cleared
P2IFG &= ~0x04; // P2.2 IFG cleared
}
*/
}
Se puede apreciar que hay más código pero que el mismo ha sido
comentado, esto es si se desea tener código individual por cada interrupción
TI MSP430
(jugando con los bits del registro P2IFG se puede lograr esto).
P á g i n a | 116
Interfaces.
28
Para aquellos que no quieran usar este puente, se puede construir uno con un MAX232 y un
adaptador RS232 a USB con este esquemático.
P á g i n a | 117
Ahora el programa:
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;
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
}
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.
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:
El programa solo consiste de dos sentencias if, cuya única función es comparar
el dato que ha sido recibido desde la computadora, dado que estos
normalmente son representados en código ASCII29, para mencionar que
queremos comparar un 1 debemos colocarlo entre apostrofes, de tal manera
TI MSP430
29
http://es.wikipedia.org/wiki/ASCII para más información.
P á g i n a | 125
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:
#include "msp430x21x2.h"
char rx,i,d,c=0;
char t[]={"Temp= XXXX C \r"};
char a[]={"Sensor de temperatura, 40 muestras promediadas \r\n"};
volatile long res,res2;
long long prom=0, suma=0;
void 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;
while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Start sampling
TI MSP430
t[9] = '0'+(res2%10); //ese %10 cambia de lo que sea a decimal (en este caso binario),
// permitiendo un despliegue de informacion
t[8] = '0'+((res2/10)%10); //en decimales, ademas de que aqui se divide
//cada parte del numero entre 10 para obtener
t[7] = '0'+((res2/100)%10); //cada caracter en unidades, decenas y centenas
while (BUSY & ADC10CTL1); //Espera a que el modulo ADC 10 termine por completo de
//Apagare
if(rx=='1')
{
TXString(t,sizeof(t));
}
else if(rx=='2')
{
TXString(a,sizeof(a));
}
else
{
TXString("\f",1); //Cualquier otra cosa limpia la pantalla
}
}
}
//Interrupcion RX
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
rx = UCA0RXBUF; // TX -> RXed character
//**********************************************Inicio Prende y Apaga P1.1
switch((P1IN&0x02))
{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; break;
}
//**********************************************Fin Prende y Apaga P1.1
}
}
P á g i n a | 128
Se pueden declarar strings completos usando los {} y los “”, esto es muy útil
cuando se requiere cambiar únicamente algunas letras del mensaje, como
para por ejemplo colocar los números de la temperatura, además de
simplificar el uso de la función TXString, que para enviar la constante entera
solo basta con teclear TXString(a, sizeof(a)); mandando la variable y su
tamaño automáticamente.
TI MSP430
P á g i n a | 129
por la división de res2 entre 100 (un numero que representa las centenas de
P á g i n a | 130
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:
#include "msp430x21x2.h"
char rx,i,d,c=0;
char t[]={"\r\nTemp= XXXX C"};
char a[]={"\r\nSensor de temperatura, 40 muestras promediadas"};
volatile long res,res2;
long long prom=0, suma=0;
void 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;
30
Y pensar que se usaba printf(“Temp= %d”,res2); en un PIC…
P á g i n a | 131
while(1)
{
ADC10CTL0 |= ENC + ADC10SC; // Start sampling
t[11] = '0'+(res2%10); //ese %10 cambia de lo que sea a decimal (en este caso binario),
// permitiendo un despliegue de información
t[10] = '0'+((res2/10)%10); //en decimales, además de que aquí se divide
//cada parte del numero entre 10 para obtener
t[9] = '0'+((res2/100)%10); //cada carácter en unidades, decenas y centenas
while (BUSY & ADC10CTL1); //Espera a que el modulo ADC 10 termine por completo de
//Apagarse
if(rx=='1')
{
TXString(t,sizeof(t));
}
else if(rx=='2')
{
TXString(a,sizeof(a));
}
else if(rx=='3')
{
TXString("\f",1); //Un 3 limpia la pantalla
}
}
}
//Interrupcion RX
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
rx = UCA0RXBUF; // TX -> RXed character
//**********************************************Inicio Prende y Apaga P1.1
switch((P1IN&0x02))
TI MSP430
{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; break;
}
//**********************************************Fin Prende y Apaga P1.1
}
P á g i n a | 132
}
}
Damos clic en la pestaña search y nos mostrara otra pantalla similar a esta:
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.
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:
Ya que tenemos espacio para trabajar, ahora si podemos usar los datos
recibidos. Presiona de nueva cuenta para ejecutar el programa y vuelve al
diagrama de bloques, posiciónate en el cable que está conectado a “read
string” y da un clic sobre el mismo:
TI MSP430
P á g i n a | 140
De esta manera se puede apreciar sobre que hilo van los datos recibidos del
RS232, ahora, para hacer un medidor realmente solo requerimos del número
en sí, no del texto, así que para paramos el programa y usamos:
Quizás no sea del todo vistosa la grafica que se muestra, pero eso se puede
arreglar de la siguiente manera: detenemos el programa, colocamos el
puntero del mouse encima de la grafica y seleccionamos properties:
TI MSP430
P á g i n a | 149
Esto concluye el uso de LabVIEW por RS232, guardamos una copia del
programa para no sobre escribir sobre el ejemplo proporcionado por el mismo
(paramos el programa primero):
P á g i n a | 152
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)
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:
31
http://es.wikipedia.org/wiki/I%C2%B2C
32
http://travisgoodspeed.blogspot.com/2008/05/repurposing-ti-ez430u-part-1.html
P á g i n a | 154
Es decir:
TI MSP430
33
http://kurt.on.ufanet.ru/
P á g i n a | 155
Para alimentar el circuito es conveniente hacerlo por USB, basta con poner un
papelito entre los dos pines de en medio del conector para que D+ y D- no
hagan contacto y así el programador no sea reconocido en Windows pero si
alimentado. Hecho eso el programador JTAG está listo para reprogramar al
microcontrolador MSP430F1612 (el chip de 64 pines, justo debajo del TUSB3410).
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.
34
PDFs\slau227c rf2500 USB Rojo Userguide.pdf
P á g i n a | 156
TI MSP430
El uso del IAR con el nuevo programador solo cambia en la selección del
microcontrolador, que para este caso será el MSP430F1612:
#include "msp430x16x.h"
char dat1[1024] = {
16 ,
52 ,
7 ,
0 ,
24 ,
217 ,
2 ,
0 ,
120 ,
78 ,
238 ,
246 ,
18
};
char dat2[1024] = {
1 ,
214 ,
233 ,
162 ,
231 ,
131 ,
240 ,
34 ,
128 ,
240
};
char dat3[1024] = {
TI MSP430
120 ,
78 ,
166 ,
24 ,
8 ,
166 ,
P á g i n a | 159
224 ,
246 ,
163
};
void main(void)
{
P1DIR = 0x00; // termination of unused pins
P2DIR = 0x00;
P3DIR = 0x00;
P4DIR = 0x00;
P5DIR = 0x00;
P6DIR = 0x00;
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?
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);
}
//Esta es la segunda parte para escribir a la eeprom, graba todos los datos restantes.
//IMPORTANTE!!!!!!!!!!!!!!! BORRAR EL CONTENIDO DEL MSP 1612 o de lo contrario este
//Volvera a escribir la eeprom, puede producir errores si no se hace.
P á g i n a | 160
#include "msp430x16x.h"
char dat1[1024] = {
8 ,
26 ,
234 ,
112 ,
248 ,
124 ,
55 ,
46 ,
192 ,
224 ,
236 ,
36 ,
0
};
char dat2[1024] = {
245 ,
130 ,
228 ,
52 ,
251 ,
245 ,
1 ,
145 ,
2 ,
133 ,
9 ,
149 ,
9 ,
117
};
char dat3[1031] = {
8 ,
37 ,
TI MSP430
1 ,
21 ,
1 ,
9 ,
1 ,
P á g i n a | 161
0 ,
0 ,
0 ,
0 ,
0
};
void main(void)
{
P1DIR = 0x00; // termination of unused pins
P2DIR = 0x00;
P3DIR = 0x00;
P4DIR = 0x00;
P5DIR = 0x00;
P6DIR = 0x00;
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?
for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i+3072,dat1[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=1023;i++)
{
EEPROM_ByteWrite(i+4096,dat2[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=1030;i++)
{
EEPROM_ByteWrite(i+5120,dat3[i]);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
for(i=0;i<=10232;i++)
TI MSP430
{
EEPROM_ByteWrite(i+6151,255);
EEPROM_AckPolling(); // Wait for EEPROM write cycle completion
}
while (1);
P á g i n a | 162
Ambos códigos a simple vista parecen muy sencillos, ya que hacen uso de
sentencias como:
/******************************************************************************/
#include "msp430x16x.h"
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
/*---------------------------------------------------------------------------*/
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
I2CWriteInit();
I2CNDAT = 3; // 1 control byte + 3 bytes should be transmitted
I2CTCTL |= I2CSTT+I2CSTP; // start and stop condition generation
// => I2C communication is started
}
P á g i n a | 165
/*---------------------------------------------------------------------------*/
unsigned char EEPROM_CurrentAddressRead(void)
// Description:
// Current Address Read Operation. Data is read from the EEPROM. The current
// address from the EEPROM is used.
{
while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations
I2CReadInit();
U0CTL |= MST; // define Master Mode
I2CNDAT = 1; // 1 byte should be received
I2CIFG &= ~ARDYIFG; // clear Access ready interrupt flag
I2CTCTL |= I2CSTT+I2CSTP; // start receiving and finally generate
// re-start and stop condition
while ((~I2CIFG)&ARDYIFG); // wait untill transmission is finished
return I2CBuffer[0];
}
/*---------------------------------------------------------------------------*/
unsigned char EEPROM_RandomRead(unsigned int Address)
// Description:
// Random Read Operation. Data is read from the EEPROM. The EEPROM
// address is defined with the parameter Address.
{
unsigned char adr_hi;
unsigned char adr_lo;
while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations
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
/*---------------------------------------------------------------------------*/
void EEPROM_AckPolling(void)
TI MSP430
// Description:
// Acknowledge Polling. The EEPROM will not acknowledge if a write cycle is
// in progress. It can be used to determine when a write cycle is completed.
{ unsigned int count;
while (I2CDCTL&I2CBUSY); // wait until I2C module has finished all operations
P5OUT ^= 0x10;
P á g i n a | 166
count=0;
U0CTL &= ~I2CEN; // clear I2CEN bit => necessary to re-configure I2C module
I2CTCTL |= I2CRM; // transmission is software controlled
U0CTL |= I2CEN; // enable I2C module
I2CIFG = NACKIFG; // set NACKIFG
while (NACKIFG & I2CIFG)
{
I2CIFG=0x00; // clear I2C interrupt flags
U0CTL |= MST; // define Master Mode
I2CTCTL |= I2CTRX; // I2CTRX=1 => Transmit Mode (R/W bit = 0)
I2CTCTL |= I2CSTT; // start condition is generated
while (I2CTCTL&I2CSTT); // wait till I2CSTT bit was cleared
I2CTCTL |= I2CSTP; // stop condition is generated after slave address was sent
// => I2C communication is started
while (I2CDCTL&I2CBUSY); // wait till stop bit is reset
count=count+1;
P5OUT ^= 0x10;
}
U0CTL &= ~I2CEN; // clear I2CEN bit => necessary to re-configure I2C module
I2CTCTL &= ~I2CRM; // transmission is by the I2C module
U0CTL |= I2CEN; // enable I2C module
return;
}
/*---------------------------------------------------------------------------*/
/* Interrupt Service Routines */
/* Note that the Compiler version is checked in the following code and */
/* depending of the Compiler Version the correct Interrupt Service */
/* Routine definition is used. */
#if __VER__ < 200
interrupt [USART0TX_VECTOR] void ISR_I2C(void)
#else
#pragma vector=USART0TX_VECTOR
__interrupt void ISR_I2C(void)
#endif
// Description:
// Byte Write Operation. The communication via the I2C bus with an EEPROM
{
switch (I2CIV)
{ case I2CIV_AL: /* I2C interrupt vector: Arbitration lost (ALIFG) */
break;
case I2CIV_NACK: /* I2C interrupt vector: No acknowledge (NACKIFG) */
break;
case I2CIV_OA: /* I2C interrupt vector: Own address (OAIFG) */
break;
case I2CIV_ARDY: /* I2C interrupt vector: Access ready (ARDYIFG) */
break;
case I2CIV_RXRDY: /* I2C interrupt vector: Receive ready (RXRDYIFG) */
I2CBuffer[0]=I2CDRB; // store received data in buffer
break;
case I2CIV_TXRDY: /* I2C interrupt vector: Transmit ready (TXRDYIFG) */
TI MSP430
I2CDRB = I2CBuffer[PtrTransmit];
PtrTransmit = PtrTransmit-1;
if (PtrTransmit<0)
{
I2CIE &= ~TXRDYIE; // disable interrupts
}
P á g i n a | 167
break;
case I2CIV_GC: /* I2C interrupt vector: General call (GCIFG) */
break;
case I2CIV_STT: /* I2C interrupt vector: Start condition (STTIFG) */
break;
}
}
Terminado esto, hay que salir del modo debug con el botón y repetir el
mismo proceso pero ahora con la pestaña y el programa:
TI MSP430
P á g i n a | 170
vía éste protocolo e inclusive memorias, dentro de las cuales las más famosas
35
http://es.wikipedia.org/wiki/Serial_Peripheral_Interface
P á g i n a | 176
son las memorias SD36 (Secure Digital) usadas en sin fin de dispositivos
electrónicos de consumo.
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.
36
http://es.wikipedia.org/wiki/Secure_Digital
P á g i n a | 177
algún ejemplo nos puede dar una idea de cómo portar el código de
microcontrolador en microcontrolador.
Nos quedaría algo así usando dos Target Boards del ez430F2013:
TI MSP430
P á g i n a | 178
37
El SDO del maestro viene conectado al SDI del esclavo (Formando un SIMO) y
el SDI del maestro se conecta con SDO del esclavo (formando un MISO).
#include "msp430x20x3.h"
void init_spi(void)
{
P1OUT |= 0x01; //LED
P1DIR |= 0x01; //Salida P1.0
37
Admito que… no es lo más estético que he hecho.
P á g i n a | 179
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}
}
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
while(1)
{
digit++;
if (digit > 100) digit = 1;
send_spi(digit); //Enviamos el digito por medio del SPI en nuestra subrutina
delay(); //Llamamos a nuestra función de retraso para observar el
//Cambio
}
}
TI MSP430
El programa por si solo viene algo explicado, sin embargo me permitiré explicar
algunos detalles:
P á g i n a | 180
Una vez configurado para SPI el micontrolador es importante quitar el bit que
agregamos anteriormente para que este pueda funcionar.
1. Se ha creado una función send_spi que admite un tipo de dato char (un
numero de 0 a 255)
2. Lo primero que se debe de hacer para enviar un dato por SPI es liberar
el Buffer (Se puede ver como un tanque de almacenamiento) para que
este pueda ser usado al momento de enviar datos, esto se logra
TI MSP430
#include <msp430x20x3.h>
unsigned int i;
unsigned char receive;
void main(void)
{
WDTCTL = WDTPW+WDTHOLD;
while(1)
{
switch((P1IN&0x01)) //Prendemos y apagamos un LED
{
case 0x01: P1OUT &=~0x01; break;
case 0x00: P1OUT |=0x01; break;
TI MSP430
}
for(i=0;i<=(receive*100);i++); //Retraso de tiempo controlado por el dato
//llegado del modulo SPI
38
PDFs\msp430f2013.pdf
P á g i n a | 182
}
}
39
Ignorar las voces de fondo…
P á g i n a | 183
Extra 1.
Extra 2.