Documente Academic
Documente Profesional
Documente Cultură
Programación
MSP430
Texas Intruments
TI MSP430
Presentado por:
Erick Noe Amezquita Lucio
Acerca de este manual:
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
µ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:
P1DI
R
P1DI 0 0 0 0 0 0 0 0
R
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
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
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
P1OU 0 0 0 0 1 0 0 0
T
¡Sas! Se tiene un 1 lógico a la salida del pin P3.1 listo para prender un LED.
P1DI 0 0 0 0 0 0 0 0
R
P1OU 0 0 0 0 1 0 0 0
T
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
P1DI 0 0 0 0 1 0 0 0
R
P1OU 0 0 0 0 0 1 0 0
T
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:
int x;
TI MSP430
void main(void)
4
¡Desu!
P á g i n a | 13
{
while(1)
/*
De esta manera
puedes colocar
varias líneas
de comentarios.
*/
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:
P á g i n a | 15
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
//a 127.
Ejemplo 5:
P á g i n a | 16
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.
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:
Ejemplo 2:
int x; //Variable global para que la función la pueda usar aun estando fuera
//de ella.
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.
Ejemplo 4:
} //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
Nota:
Ejemplo 5:
int x;
void main(void)
{
int j;
while(1)
{
multiplica(20);
j=x;
}
}
TI MSP430
Ejemplo 6:
int x;
void main(void)
{
int j;
while(1)
{
multiplica(20);
j=x;
}
Ejemplo 1:
#include “msp430x21x2.h"
#define Verdad 1
#define Falso 0x01
Operadores Lógicos:
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.
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.
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 + - * /
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:
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:
Corrimient Corrimient
o a la o a la
derecha izquierda
Corremos Corremos
8 espacios 8 espacios
a la a la
TI MSP430
derecha un izquierda
registro un registro
P á g i n a | 25
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
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
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).
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
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
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];
Ciclos infinitos:
Ejemplo 1:
TI MSP430
{
//Codigo.
}
}
Ejemplo 2:
Ejemplo 3:
Condicionales:
Sentencia if
Ejemplo 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
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
{ //mucho las condiciones descritas más arriba.
break; //Salir del ciclo infinito
}
Else //De cualquier manera
{
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;
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;
}
}
}
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)
z=z+i; //Incrementa el valor de z en z+i (como si hiciéramos una sumatoria, z
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
valor de inicio, se puede jugar con ellos a voluntad para resolver alguna
necesidad en particular.
Sentencia while
P á g i n a | 33
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.
}
}
Las condiciones se pueden usar en los if’s, case’s, y while’s por igual.
Sentencia do + 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
void main(void)
{
while(1)
{
do
{
j++;
}while( !( (j<=5) | (j>=10) ) ); //Condición ‘Mientras j sea diferente de <=5 ó >=10’
x++;
}
}
6 Ciclo do while 5 6
7 Ciclo do while 5 7
8 Ciclo do while 5 8
9 Ciclo do while 5 9
par.
P á g i n a | 35
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.
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
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
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:
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
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 | 45
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
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
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):
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.
del LED…
P á g i n a | 52
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
{
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í:
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 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
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
.
18
No quería decirlo pero… el inglés es realmente importante aquí.
P á g i n a | 63
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 | 64
1 2
3 4
#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
TI MSP430
0 0 0 1 0 0 0 0
0 0 1 1 0 0 0 1
0 0 1 1 0 0 0 0
#include "msp430x21x2.h"
{
// 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;
}
1 2
TI MSP430
3 4
Explicando el programa:
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…)
~0x01
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:
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).
21
Y me acordaba del… read_adc();
P á g i n a | 72
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);
// LPM0 (Low Power Mode 0) Modo de ahorro de energia +
//GIE (General Interrupt Enable) Habilitacion de interrupciones
return 0;
}
1 2
3 4
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.
P á g i n a | 74
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).
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.
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)
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.
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
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)
{
__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;
}
(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)
TI MSP430
__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 eso la resta de 986.
Entonces:
Con esta práctica se concluye el uso de los ADCs. La misma la tienes aquí.
P á g i n a | 88
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;
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*/
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
}
}
}
{
res = ADC10MEM; // De aqui obtenermos el resultado del ADC.
ADC10CTL0 &= ~ENC; // Apagamos el ADC
}
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 GuideError: Reference source not found (en
la página 406) del microcontrolador que estamos usando:
TI MSP430
#include "msp430x21x2.h"
volatile unsigned int res;
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;
}
#include "msp430x21x2.h"
volatile unsigned int res;
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;
}
Y a continuación el programa:
{
IFG1 &= ~OFIFG; // Clear OSC fault flag
i = 0xFF; // i = Delay
while (i--); // Additional delay to ensure start
}
P á g i n a | 99
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;
}
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
Los parámetros que se configuraron para el uso del ADC cambiaron un poco
para hacer más estable al PWM.
#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
while(1)
{
for(i=0;i<=10000;i++);
switch((P1IN&0x04))
{
case 0x04: P1OUT &=~0x04; break;
case 0x00: P1OUT |=0x04; break;
}
}
}
{
case 0x02: P1OUT &=~0x02; break;
case 0x00: P1OUT |=0x02; 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.
El programa aquí.
Código:
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.
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:
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
//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;
__bis_SR_register(GIE); // Enter LPM4 w/interrupt
while(1)
{
ADC10SA = (unsigned int)&res; // Data transfer location
ADC10CTL0 |= ENC + ADC10SC; // Start sampling
}
}
/*
//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
}
*/
}
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).
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 | 114
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.
29
http://es.wikipedia.org/wiki/ASCII para más información.
P á g i n a | 123
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"
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 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
t[9] = '0'+(res2%10); //ese %10 cambia de lo que sea a decimal (en este caso binario),
P á g i n a | 125
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
}
}
}
TI MSP430
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
#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
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
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
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
}
}
}
//la conversión.
__interrupt void ADC10_ISR(void)
{
res=ADC10MEM;
ADC10CTL0 &= ~ENC; // Apagamos el ADC
P á g i n a | 130
Damos clic en la pestaña search y nos mostrara otra pantalla similar a esta:
TI MSP430
Advanced Serial Write and Read.vi. Esto nos llevará a otra ventana, ya
con el ejemplo abierto:
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 | 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:
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
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
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
Ingredientes:
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
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).
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 | 153
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:
#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] = {
120 ,
78 ,
166 ,
24 ,
TI MSP430
8 ,
166 ,
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
TI MSP430
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 ,
1 ,
21 ,
1 ,
9 ,
1 ,
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;
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++)
{
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
/******************************************************************************/
#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
TI MSP430
/*---------------------------------------------------------------------------*/
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
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)
// 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
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
break;
}
}
while(1): y presionaremos :
TI MSP430
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
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.
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.
microcontrolador en microcontrolador.
36
http://es.wikipedia.org/wiki/Secure_Digital
P á g i n a | 174
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).
#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 | 176
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
#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;
}
for(i=0;i<=(receive*100);i++); //Retraso de tiempo controlado por el dato
//llegado del modulo SPI
}
}
#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
Extra 1.
39
Ignorar las voces de fondo…
P á g i n a | 180
Extra 2.