Mostrando entradas con la etiqueta Programacion. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programacion. Mostrar todas las entradas

jueves, 23 de marzo de 2017

Usar asm (esamblador) en un sketch de programación para Arduino

En la necesidad de optimizar un programa que fuera más rápido en la ejecución de un algoritmo, ya que usa los recursos para cifrar archivos, busque en la red como reducir el número de instrucciones que el procesador deberá realizar para ciertas tareas de la encriptación  busque como utilizar el lenguaje ensamblador insertado en el código de lenguaje C, y utilizando el entorno de programación del Arduino, buscando en las redes me enteré que la programación del Arduino hace uso de las librerías de lenguaje C de proyecto de código libre AVR y dentro de sus páginas web, encontré el manual de uso para insertar ensamblador(asm), dentro del código de lenguaje C, Inline Assembler Cookbook, aqui esta en inglés es un manual que dicho por su propio autor no esta completo, pero es una buena base para entender la forma de insertar ensamblador dentro del código C, y por lo tanto dentro de la programación del Arduino, después de tratar varios ejemplos y no logar mas que al compilar marcara errores en la programación, y navegando un poco mas empece a lograr poner ciertas lineas sin que aparecieran errores, es por eso que me di a la tarea (dentro de mis limitaciones tanto del inglés como de programación) de primeramente hacer una traducción  de el manual AVR sobre el uso de lenguaje ensamblador dentro del lenguaje C
Ya con la traducción y con un poco mas de entendimiento pude dentro del entorno de Arduino usar ensamblador en el programa sencillo .

En la imagen parte del sketch de ejemplo.
Este  genera el código similar al que se generado con el básico, Blink, que hace destellar (enciende y apaga) el led conectado a el pin 13 de la placa de Arduino, pero usando el lugar de la función delay( ), la función llamada asm_delay,que esta escrita, con instrucciones de ensamblador. La declaración del nombre de la función, es parte del lenguaje c y no hay cambios, el uso de uint8_t en el tipo de variable usada para el pasar el argumento, así uint8_t ms, es una variable de 8 bits sin signo es similar a escribir unsigned char ms, algo similar al declarar el tipo uint16_t solo que este es de 16 bits sin signo.
La declaración en lenguaje c que le indica al compilador que se hará uso de ensamblador, es la declaración asm volatile, esta le indica al compilador, que lo que seguirá entre paréntesis son instrucciones escritas en ensamblador y datos asociados a estas instrucciones, como son registros, o valores numéricos, su escritura general como si se usara una función pero con ciertas características especiales :

     asm volatile( : : : );

Esta sentencia esta seccionada en 4 partes divididas por la escritura de dos puntos ":" entre ellas, la primera, parte son las instrucciones que son leídas como cadenas de caracteres, escritas entre comillas, por ejemplo:

     "mov %A0 %A2"

la segunda parte los operandos de salida,  la tercera parte son los operandos de entrada , la ultima parte es opcional y en este ejemplo no es necesaria.
Así todo lo que esta entre la declaración de uso de ensamblador  asm volatile( );, le indica al compilador que son instrucciones en ensamblador.
Las instrucciones en ensamblador;
"L_dl1%="  "mov %A0, %A2" "mov %B0, %B2" L_dl2%" "dec %1" " brne L_dl!%="
provienen del conjunto de instrucciones AVR de Atmel ya que los procesadores de Arduino usan chips con este conjunto de instrucciones, ya que son fabricados por Atmel así por ejemplo:
"mov %A0, %A2"
lo encontramos como
Donde mov es la instrucción,  %A0 es la referencia a un registro en este caso Rd (registro destino) donde sera copiado %A1 que hace referencia el referencia al registro Rr que es el registro de donde sera copiado el dato, para incrustar código ensamblador dentro de c no se escribe literalmente como se hace al escribir un programa en lenguaje ensamblador exclusivamente. recomiendo que lea la traduccion del manual para entender como se hace.
Pero, vamos por partes, en la lista de instrucciones (primera parte):

"L_dl1%=:" "\n\t"
"mov %A0, %A2" "\n\t"
"mov %B0, %B2" "\n"
"L_dl2%=:" "\n\t"
"sbiw %A0, 1" "\n\t"
"brne L_dl2%=" "\n\t"
"dec %1" "\n\t"
"brne L_dl1%=" "\n\t"


Todas las instrucciones están puestas como cadenas de caracteres, por lo que están entre comillas, así que tenemos 16 cadenas de caracteres. La primera cadena de caracteres "L_d11%=:", es la referencia de una etiqueta, que debe ser única, así la literal L_ %=: indica una etiqueta, para ensamblador d11 es lo que diferencia la etiqueta de otra como d12, por lo que, "L_d11%=:" y "L_d12%=:" son etiquetas en ensamblador. La segunda cadena "\n\t" solo le indica al compilador que ponga una nueva linea \n y luego que haga un tabulador \t esto solo es para organizar nuestro codigo y que sea mas facil de entender, podemos quitarlas pero debemos poner todas las instrucciones en una sola linea de otra forma nos marcara errores. La tercera cadena "mov %A0, %A2"  es la instrucción  MOV Rd,Rr que copia el valor que hay en el registro Rr al registro Rd. , los valores o registros que serán usados en las instrucciones no se colocan directamente sino solo una referencia a ellos, esto es el caso de %A0,%A2, solo son referencias de las variables o valores usados, estos registros o variables están en la segunda y tercera parte de la sección de asm, recuerde que los separadores de dos puntos dividen la sección de asm, hasta en 4 partes, la segunda están los registros que serán escritos:
: "=&w" (cnt) 
y en la tercera los que serán leídos 
: "r" (ms), "r" (delay_count)
Ya que las referencias se van numerando desde el cero en adelante, aumentado de uno, a la variable cnt que es la primera en aparecer en la lista, se le asigna  %0, pero ya que es una variable de dos bytes (uint16_t) a cada byte se le asigna una letra iniciando con la letra A mayúscula, así el primer byte de la variable cnt se referencia como %A0, y el segundo byte como %B0, si fuera el caso y la variable sea de 4 bytes las referencias serían %A0, %B0, %C0, y %D0. La segunda variable que aparece en la lista es ms por lo que le corresponde %1, la tercera variable es delay_count como también es de 2 bytes de longitud, le corresponden las referencias, %A2 y %B2, asi que:
cnt                 %A0 %B0
ms                 %1
delay_count   %2A %2B
La quinta cadena es similar a la tercera, y la  sexta a la primera, la novena cadena "sbiw %A0, 1"  resta en 1 a la variable cnt, que solo esta referenciado el primer byte pero la instruccion sbiw es una instruccion que resta en los 2 bytes de la variable. La onceava cadena "brne L_dl2%="  salta si el resultado de la anterior operacion no fue cero hacia donde esta la etiqueta L_d12 y lo mismo hace la 15ª cadena pero hacia la etiqueta L_d11 , la cadena 13ª decrementa en 1 la variable ms uqe es la que tiene referenciada como %1.

El código completo de ejemplo, esta en seguida, se cargo en un Arduino Mega2560, pero el funcionamiento debe ser igual para el Arduino uno, o cualquier otra placa de Arduino, como es ensamblador hay que tener cuidado con los registros especiales que tiene cada chip usado por las diferentes placas de Arduino ya que  algunos no tiene los mismos registros especiales o las mismas capacidades de memoria , puertos o pines especiales como la ubicación del puerto serial, y otras características de cada uno de ellos, aunque en este ejemplo no debe haber problemas, siempre es bueno revisar las características del micro controlador.
El programa para el sketch en IDe de Arduino:



/*  retardo_asm
Este programa de arduino genera el codigo
similar al que se generado con el Basico de
Blink, que hace destellar (enciende y apaga) el
led conectado a el pin 13 del arduino
el código asm para este sketch se obtuvo de:
http://www.nongnu.org/avr-libc/user-manual/inline_asm.html
y una traducción al español en:
http://numeroscelestes.blogspot.mx/2017/03/instrucciones-de-ensamblador-dentro-de.html
que pueda servir como ejemplo de
inserción de código ensamblador, dentro del lenguaje C
*/
#define FRECUENCIA_RELOJ_CPU 16000000

uint16_t delay_count;      // la definicion del tipo de dato uint8_t es similar a usar  unsigned char
void asm_delay(uint8_t ms);   // prototipo de la función

void setup() {
   // inicializa como salida digital el pin 13.
  pinMode(13, OUTPUT);
  delay_count= FRECUENCIA_RELOJ_CPU/4000;

}

void loop() {
  digitalWrite(13, HIGH);   // Enciende el LED 
  asm_delay(250);           // función de retardo con ensamblador 
  digitalWrite(13, LOW);   // Apaga el LED 
  asm_delay(250);
  
}
/*------------------------------
retardo usando instrucciones en ensamblador (assambly)
*/
void asm_delay(uint8_t ms)
{
uint16_t cnt;      // variable cnt,tipo de datos de 2 bytes sin signo
asm volatile (
"\n"
"L_dl1%=:" "\n\t"
"mov %A0, %A2" "\n\t"
"mov %B0, %B2" "\n"
"L_dl2%=:" "\n\t"
"sbiw %A0, 1" "\n\t"
"brne L_dl2%=" "\n\t"
"dec %1" "\n\t"
"brne L_dl1%=" "\n\t"
: "=&w" (cnt)
: "r" (ms), "r" (delay_count)
);
}
// fin del programa ///
//////////////////////////////// /* una rutina equivalente para lenguaje ensamblador
    como ejemplo solamente
////////////////////////////////
#define delay_count 0C904h asm_delay: bucle_externo:   mov  r24, low(delay_count)  ; carga el valor en el par    mov  r25, high(delay_count) ; de registros r24_r25 bucle_interno:   sbiw r24,1                  ; resta 1 al par de registros r24_25   brne bucle_interno          ; No es cero regresa hacia bucle_interno   dec  ms                     ; decrementa la variable ms   brne bucle_externo          ; No es cero regresa hacia bucle externo   ret                         ; regresa de la subrutina */

jueves, 22 de mayo de 2014

Adaptacion para usar una PC como estacion de internet con pago a traves de monedas

Con la idea de realizar la adaptación para el control de tiempo en una maquina de Internet depositando monedas. Ya hay en el mercado maquinas que haciendo esto pero con el deseo de hacerla por mi, me puse la tarea de realizar una estación que fuera funcional.
Par la estación el uso de una PC de escritorio, el control se realiza manipulando la alimentación de el teclado y el ratón, se usan dispositivos USB, y para habilitarlos cuando se tenga tiempo disponible e inhabilitarlos al finalizar el tiempo, también se envían pulsos para reiniciar la tarjeta madre de la PC y vuelva estar disponible otra vez para un nuevo usuario inicie una sesión.
El tiempo que el usuario tendrá cada vez que deposite una moneda se presentara en 4 displays de 7 segmentos.
Ya que entre las chacharas tenía descartada una placa con 4 diplays de una tableta de play choice( Un juego de nintendo para maquinas operadas con monedas, ya en desuso ), y que podía servir bastante bien para mi propósito, me di a la tarea de hacerle un pequeño esquema para poder, utilizarlo.
Revisando el diagrama me di cuenta de que las conexiones que salían y entraban de placa de displays eran por un conector de 12 cables,  los primeros 4 pines del conector se usaban para transmitir los datos hacia los cuatro integrados 4511 que son decodificadores de BCD a 7 segmentos con latch, los siguientes tres entradas son las entradas del decodificador de 3 a 8 (74LS138), y que sirve para seleccionar el 4511 en que los datos serán guardados, el siguiente pin del conector(8) se usa para habilitar el chip de selección, y  los últimos 4 pines son para las conexiones de tierra y +5 volts que alimentan el circuito 

Como me pareció que el cableado seria demasiado 12 cables me di a la tarea de reducirlo a 3 (aunque el conector que utilice es de 4), y hacerlo así mas sencillo de cablear.

Decidí quitar el decodificador (74LS138) ,  y en su lugar se colocar un micro-controlador PIC16F687, haciendo  mas sencillo el control ya que a través de la comunicación  serial asíncrona, solo se utiliza un cable para enviar la información al pic y este se encarga de escribirla en los displays, así que la placa solo queda conectada a tierra, +5volts y el cable de recepción de datos. Use este esquema para sustituirlo, se ven las conexiones del PIC y del decodificador LS138, unidas para saber cuales pines si deben soldarse en los lugares que tenia el que se quita y cuales no.
En las siguientes fotos se miran las adaptaciones hechas, primero quitar el decodificador (74LS138)...
... luego  poner una cables y una base para insertar el PIC, debido a que para la programación, se grababa el pic y luego se insertaba para ir probando el programa, hasta que quedo como se deseaba  aqui la imagen de la placa con el pic en su base
Después se recortaron los pines no necesarios y se quito la base, se soldó el pic ya programado en su lugar, cableándose algunos de los pines como los del voltaje y la tierra, quedando completada la adaptación
de la placa.

La información que recibe la placa de displays,( solo recibe no transmite) esta configurada para recibir los datos en serie a velocidad de 19200 bits por segundo, 8bits de datos sin paridad y con un bit de parada.
Los datos son recibidos es grupo de cinco bytes, en codigo ASCII, el primero que es el que espera el micro es una letra "t" y mientras no la reciba no hace nada, aunque reciba datos este los desecha, luego de recibir la "t" espera cuatro datos uno para cada display, estos solo pueden presentar números del 0 al 9 , si es enviado cualquier otro carácter, diferente a  los números del 0 al 9 el display de apaga.
La secuencia es entonces  letra "t" y en seguida los datos de los displays empezando con el de menor
valor hasta el de mayor valor.
Se le agrego una rutina de prueba de los displays que va presentando los números del 0 al 9 en secuencia durante un fracción de segundo, esto solo es al encender el display , y después de unos segundos ya esta listo para recibir datos y presentarlos.
El código fue hecho en CCS,
//
// AdaptPC.c
//        hardware: pic16f687
// Adaptación de un display de Play Choice
// para recibir los datos de presentación a través de
// puerto serie RX y su presentación en los displays
// Son 4 displays de 7 segmentos para números desde 0 hasta 9999
// vienen con descodificadores de 4 bits para 7 segmentos, con Latch
// El formato de recepción de datos es de cinco datos en ASCII empezando con
// la letra 't' y luego los datos de seg, dseg, min y dmin que corresponden a los
// cuatro displays, empezando de derecha a izquierda,
// los datos validos solo son los números del 0 al 9 y la letra 't'
// si se coloca un dato no valido el display no presentara nada estará apagado
//
/////////////////////////////////////////////////////////
#include <16F687.h>
#fuses INTRC_IO,NOWDT,NOPROTECT,NOMCLR
#use delay(clock=4000000)
#use rs232(baud=19200, xmit=PIN_B7,rcv=PIN_B5)

//constantes
const char D0 = PIN_C3, D1 = PIN_C5, D2 = PIN_C6, D3 = PIN_C4, Recibir = PIN_B5 ;

//variables
char  DISP1 = PIN_A4, DISP2 = PIN_A5, DISP3 = PIN_A1, DISP4 = PIN_A0;
char seg = 0, dseg = 0, min = 0, dmin = 0;
short b_transmision = 0;

///////////////////////////////////////////////
void pulsoEn(char xdisplay){
output_low(xdisplay);
output_high(xdisplay);
}
//////////////////////////////////////////////
void Inicializa(){
delay_ms(1);
output_high(D0);
output_high(D1);
output_high(D2);
output_high(D3);
pulsoEn(DISP1);
pulsoEn(DISP2);
pulsoEn(DISP3);
pulsoEn(DISP4);
}
///////////////////////////////////////////////
void colocaDato(char dato){
switch (dato) {
    case 0:output_low(D0);
output_low(D1);
output_low(D2);
output_low(D3);
          break;
    case 1:output_high(D0);
output_low(D1);
output_low(D2);
output_low(D3);
          break;
    case 2:output_low(D0);
output_high(D1);
output_low(D2);
output_low(D3);
          break;
    case 3:output_high(D0);
output_high(D1);
output_low(D2);
output_low(D3);
break;
    case 4:output_low(D0);
output_low(D1);
output_high(D2);
output_low(D3);
          break;
    case 5:output_high(D0);
output_low(D1);
output_high(D2);
output_low(D3);
          break;
    case 6:output_low(D0);
output_high(D1);
output_high(D2);
output_low(D3);
          break;
    case 7:output_high(D0);
output_high(D1);
output_high(D2);
output_low(D3);
          break;
    case 8:output_low(D0);
output_low(D1);
output_low(D2);
output_high(D3);
          break;
    case 9:output_high(D0);
output_low(D1);
output_low(D2);
output_high(D3);
          break;
    default:output_high(D0);
output_high(D1);
output_high(D2);
output_high(D3);
            break; }
}
///////////////////////////////////////////////
void escribeDatoEn(char display, char dato){
colocaDato(dato);
pulsoEn(display);
}
///////////////////////////////////////////////
void actualizaDisplay(){
if (dmin == 0){
escribeDatoEn(DISP4,15);
}
else escribeDatoEn(DISP4,dmin);
if ((dmin == 0) &&(min == 0)){
escribeDatoEn(DISP3,15);
}
else escribeDatoEn(DISP3,min);
if ((dmin == 0) &&(min == 0) && (dseg == 0)){
escribeDatoEn(DISP2,15);
}
else escribeDatoEn(DISP2,dseg);
escribeDatoEn(DISP1,seg);
}
///////////////////////////////////////////////
void autoPrueba(){
while(seg < 10){
actualizaDisplay();
delay_ms(300);
seg++; min++; dseg++; dmin++;
}
seg = 0; dseg =0; min = 0; dmin = 0;
actualizaDisplay();
}
///////////////////////////////////////////////
void asignaDatos (){
char aux ;
aux = getc();
if (aux == 't'){
aux = getc();
seg = (aux - 48);
aux = getc();
dseg = (aux - 48);
aux = getc();
min = (aux - 48);
aux = getc();
dmin = (aux - 48);
}
}
///////////////////////////////////////////////
void main (){
Inicializa();
autoPrueba();
while(TRUE){
delay_ms(1);
asignaDatos ();
actualizaDisplay();
}
}




sábado, 4 de enero de 2014

El código abierto en Microcontroladores


En la foto el Arduino Uno
Algunos de los proyectos con PICs
porgramador para PICs
PIC16F877A
Yo me inicie en la programación de micro-controladores, con los pic de Microchip, durante mucho tiempo con el PIC16F84 siempre usando ensamblador asm para hacer proyectos pequeños, un contador de monedas un control de tiempo y algunos otros; luego con el pic18f4450 ,  conocí el lenguaje C para micro el C18 y el  CCS haciendo mas fáciles los proyectos, pero ahora empece mi búsqueda de código abierto (open source) al escuchar sobre Arduino.
La información del código abierto es de dominio público, y puede ser usada y modificada por cualquier persona en cualquier parte del mundo, me pareció muy atractivo, luego al conocer sus productos, vi que también había mucho  hardware disponible para ser usado y programado rapidamente, no solo el código sino las especificaciones técnicas para muchas cosas, de ahí empezó esta nueva pasión.


Al principio no conocía nada de Atmel y sus procesadores ya que había trabajado con los PICs pero al ir navegando y conociendo nuevas cosas llegue a Arduino y en su pagina vi la aplicación para trabajar con las tarjetas de Arduino, solo la descargue con la finalidad de conocer un poco mas antes de comprar alguna tarjeta y al ver los ejemplos y la facilidad para programar me llamo la atención empezando ver el funcionamiento y las diferencias entre estas, asi investigando me llamo mucho la atención el código abierto propuesto por Arduino, y luego intentado encontrar algo similar en los PICs encontré la pagina de Amicus que es una tarjeta con las mismas proporciones y funciones que la placa de del Arduino

Foto de la placa de Amicus

programador par micros Avr


En esta búsqueda del funcionamiento de estas tarjetas, encontré lo que es el bootloader usado para el PIC16F877, lo grabe con el software que descargue directamente de Microchip y empece a programar ya sin necesidad del programador solo con la conexión de puerto serial de la computadora solo agregandole un chip MAX232

PIC16F877A con bootloader y circuito max232 para comunicarse con la computadora a traves del puerto serie.