Material Primer ejemplo

Anuncio
Guía de práctica
¿Que queremos hacer?
Vamos a hacer el “Hello World” del mundo de la electrónica que no es más que prender y
apagar un LED a intervalos regulares. PROYECTO LAPEGÜE
1 = Prende
Dispositivo
0 = Apaga
¿Como lo vamos a hacer?
Vamos a utilizar un microcontrolador como dispositivo y desarrollaremos un programa en
assembler que cumpla con la lógica esperada.
Debemos seleccionar:
1.
2.
3.
4.
1. Que microcontrolador vamos a usar
2. Que frecuencia de CPU va a tener
3. En que pata o pín estará conectado el Led
4. La frecuencia de encendido del led
Utilizaremos el microcontrolador de ATMEL ATMega 328 por ser uno de los más utilizados
hoy en día.
La frecuencia será de 8 MHz
elegiremos la salida en la pata 14 que corresponde al PUERTO B, particularmente el bit 0
(PB0)
La frecuencia será de 1Hz y debe estar 500ms encendido y 500ms Apagado
¿Y ahora?
Bueno lo primero que hay que hacer indefectiblemente es estudiar la arquitectura del dispositivo,
a diferencia de la programación clásica en PCs (comúnmente arquitectura i386 o AMD64 FIJA) en
la programación de uC lo que cambia es justamente la arquitectura en función del dispositivo
elegido, y el desarrollo del software tiene que “machear” perfectamente con ella.
En este caso el dispositivo elegido es el ATMega328
Idealmente uno debe ir al datasheet http://www.atmel.com/devices/atmega328.aspx ahí es
donde están todas las especificaciones del dispositivo, en mi experiencia personal eh perdido
horas y horas buscando en internet como se configura tal o cual periférico por miedo a leer el
tenebroso datasheet , pero cuando lo lees te das cuenta que todo esta ahí de la forma más clara y
sintética que puede ser explicado. Aunque son 555 hojas para este dispositivo no hay que leerlo
todo, sino solo lo que se necesita.
Datasheet
¡A desarrollar!
Entorno de desarrollo de software: AVRStudio
Project->New Project y seleccionar AVR ASEMBLER, poner el nombre de proyecto “lape” y en
initial file poner main, luego en Location elegir la carpeta donde se creará el proyecto.
NEXT-> Seleccionar AVR Simulator 2 en el lado izquierdo y ATmega328 en el derecho, en este
punto estamos seleccionando el microcontrolador que vamos a utilizar (en este caso a simular).
FINISH-> y listo ya tenemos nuestro proyecto creado.
¿Que es lo primero que tenemos que incluir? (no vale mirar abajo)
Si, el archivo que hace la redefinición de nombre de los registros, podríamos programar esto
sbi 0x25, 2
Que funcionaria bien, pero claramente es mejor escribir la sentencia de la siguiente manera
sbi PORTB,2 // set bit 2 in register 0x25 (que para el ATmega328 es el puerto B )
Esto permita que el código sea mucho más portable que el primer ejemplo. Por lo tanto
incluyamos el archivo de definiciones.
.include <m328def.inc>
loop_for_ever:
rjmp loop_for_ever
Una vez que agregamos esto vamos a ensamblar el código
todo ok?
En este punto tenemos un programa que se puede bajar al micro (aunque como se ve no hace
nada) si vamos a “Project Patht”/lape se puede ver los archivos generados. Abrir el .hex con el
block de notas…
Continuemos ahora asignando algunos nombres a registros, que utilizaremos como variables, pero
tiene que quedar bien en claro que no son variables sino registros de hardware, o sea que no
están en memoria RAM.
Debajo de la asignación de nombres agregamos el vector de interrupciones. Analizar la estructura
y la secuencia de ejecución.
.include <m328def.inc>
.def Temp1 = r17
.def Temp2 = r18
.def Temp3 = r19
.def data
.def byte
= r20
= r21
.equ INPUT
.equ OUTPUT
= 0x00 ;
= 0xFF ;
.org 0x0000
rjmp RESET
rjmp EXT_INT0
rjmp EXT_INT1
rjmp PC_INT0
rjmp PC_INT1
rjmp PC_INT2
rjmp WDT
rjmp TIM2_COMPA
rjmp TIM2_COMPB
rjmp TIM2_OVF
rjmp TIM1_CAPT
rjmp TIM1_COMPA
rjmp TIM1_COMPB
rjmp TIM1_OVF
rjmp TIM0_COMPA
rjmp TIM0_COMPB
rjmp TIM0_OVF
rjmp SPI_STC
rjmp USART_RXC
rjmp USART_UDRE
rjmp USART_TXC
rjmp ADC_RDY
rjmp EE_RDY
rjmp ANA_COMP
rjmp TWI
rjmp SPM_RDY
EXT_INT0:
EXT_INT1:
PC_INT0:
PC_INT1:
PC_INT2:
WDT:
TIM2_COMPA:
TIM2_COMPB:
TIM2_OVF:
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
Reset Handler
IRQ0 Handler
IRQ1 Handler
PCINT0 Handler
PCINT1 Handler
PCINT2 Handler
Watchdog Timer Handler
Timer2 Compare A Handler
Timer2 Compare B Handler
Timer2 Overflow Handler
Timer1 Capture Handler
Timer1 Compare A Handler
Timer1 Compare B Handler
Timer1 Overflow Handler
Timer0 Compare A Handler
Timer0 Compare B Handler
Timer0 Overflow Handler
SPI Transfer Complete Handler
USART, RX Complete Handler
USART, UDR Empty Handler
USART, TX Complete Handler
ADC Conversion Complete Handler
EEPROM Ready Handler
Analog Comparator Handler
2-wire Serial Interface Handler
Store Program Memory Ready Handler
TIM1_CAPT:
TIM1_COMPA:
TIM1_COMPB:
TIM1_OVF:
TIM0_COMPA:
TIM0_COMPB:
TIM0_OVF:
SPI_STC:
USART_RXC:
USART_UDRE:
USART_TXC:
ADC_RDY:
EE_RDY:
ANA_COMP:
TWI:
SPM_RDY:
reti
loop_for_ever:
rjmp loop_for_ever
¿Que falta?
Ahora agreguemos el siguiente fragmento de código
RESET:
ldi
out
ldi
out
sei
r16, high(RAMEND); Main program start
SPH,r16 ; Set Stack Pointer to top of RAM
r16, low(RAMEND)
SPL,r16
; Enable interrupts
En este punto se setea donde comienza la pila de programa (STACK), y se habilitan las
interrupciones.
Hasta el momento tenemos lo que llamaríamos un template de programa, ahora hay que escribir
la lógica. Comenzaremos escribiendo las rutinas de retardo, como no estamos utilizando
interrupciones ni timers la única forma de crear un retardo es a través de espera ocupada (Busy
Wait) del CPU. Para esto hay que saber principalmente 2 cosas
1) Frecuencia de CPU
2) Tiempo que tardan en ejecutarse cada instrucción del set.
La frecuencia la escogemos nosotros, por ejemplo 4MHz, y para los tiempos de instrucción
podemos ir al datasheet ( Instruction Set Summary) y ver cuanto tarda cada instrucción
involucrada en la rutina. Un ejemplo a continuación.
Comenzaremos realizando un retardo de 5 ms
;-------------------------------------------------------------;Rutinas de Retardo de 5ms
;-------------------------------------------------------------; hacer un llamado tarda 5 ciclos
delay5ms:
ldi Temp1, 66 ;para 8mhz ; 1 ciclo
LOOP0:
ldi temp2, 200 ; 1 ciclo
LOOP1:
dec temp2 ; 1 ciclo
brne LOOP1
; 1 si es falso 2 si es verdadero
dec Temp1 ; 1
brne LOOP0
; 2
ret
;-------------------------------------------------------------Llamada 5
Ldi 1
1
LDI 2
1
dec 1
1
brne 1 2
dec 2
1
brne 2 2
ret
4
*
*
*
*
*
*
*
*
1
1
66
200
200
66
66
1
Tiempo= (5+1+66+(200+400)*66+66+132+4)/8000000= 0,00498seg ~= 5ms
De la misma manera procedemos para hacer un retardo de 0,5 seg utilizando la rutina anterior
;-------------------------------------------------------------;Rutinas de Retardo de 500ms
;-------------------------------------------------------------delay500ms:
ldi temp3,100
;para 8mhz
LOOP500ms:
call delay5ms
dec
temp3
brne
LOOP500ms
ret
;-------------------------------------------------------------También podían utilizar la aplicación AVRdelayloop.exe que les hace estos cálculos
automáticamente ;) .
Ahora hay que inicializar el puerto escogido como salida. y por último PRENDER y APAGAR el LED
RESET:
ldi
out
ldi
out
ldi
out
Temp1,
low(RAMEND)
SPL, Temp1
Temp1,
high(RAMEND)
SPH, Temp1
temp1,0xFF
DDRB,temp1
sei
;********************************************
;
Bucle Principal
;********************************************
final:
call delay500ms
sbi
PORTB,0
call delay500ms
cbi
PORTB,0
rjmp final
NOTA: Leer “Newbies guide to AVR development.pdf”
Como último paso hay que debuggear, así que podemos hacerlo apretando
movemos por el código. si queremos algo más pro Proteus!!!
con F11 nos
Descargar