¿Cómo puede alguien usar un microcontrolador que tiene solo 384 bytes de memoria de programa?

11 respuestas

131

¡Niños, salgan de mi césped!

384 bytes es un montón de espacio para crear algo bastante complejo en el ensamblador.

Si recorres la historia cuando las computadoras tenían el tamaño de una habitación, encontrarás algunas hazañas de arte realmente sorprendentes ejecutadas en < 1k.

Por ejemplo, lea el clásico Story of Mel - A Real Programmer . Es cierto que esos tipos tenían 4096 palabras de memoria con las que jugar, los infieles decadentes.

También mire algunas de las antiguas competiciones de demoscene en las que el desafío era colocar una "introducción" en el bloque de arranque de un disquete, los objetivos típicos son 4k o 40k y, por lo general, logran incluir música y animación.

Editar para agregar : Resulta que puede implementar la primera calculadora científica de $ 100 del mundo en 320 palabras.

Editar para los jóvenes 'uns:

  • Floppy = disquete.
  • Bootblock = 1er sector del disquete leído en el arranque.
  • Demoscene = competiciones de programación entre grupos de hackers.
  • Ensamblador = manera elegante de programar un dispositivo si eres demasiado suave para usar 8 interruptores y un botón "almacenar".
respondido por el John U
57

Los microcontroladores son lo suficientemente baratos como para que a menudo se utilicen para hacer cosas realmente simples que en años pasados probablemente se habrían hecho con lógica discreta. Cosas realmente simples. Por ejemplo, uno podría querer que un dispositivo active una salida durante un segundo cada cinco segundos, más precisamente de lo que un temporizador 555 podría hacer.

  movwf OSCCON
mainLp:
  ; Set output low
  clrf  GPIO
  movlw 0xFE
  movwf TRIS
  clrwdt
  call  Wait1Sec
  clrwdt
  call  Wait1Sec
  clrwdt
  call  Wait1Sec
  clrwdt
  call  Wait1Sec
  ; Set output high
  bsf   GPIO,0
  clrwdt
  call  Wait1Sec
  goto  mainLp
Wait1Sec:
  movlw 6
  movwf count2
  movlw 23
  movwf count1
  movlw 17
  movwf count0
waitLp:
  decfsz count0
   goto   waitLp
  decfsz count1
   goto   waitLp
  decfsz count2
   goto   waitLp
  retlw  0

Esa sería una aplicación real, utilizable, en menos de 32 palabras (48 bytes) de espacio de código. Uno podría agregar fácilmente algunas opciones para que algunos pines de E / S controlen las opciones de temporización y aún tenga mucho espacio de sobra, pero incluso si todo el chip fuera exactamente lo que se muestra arriba podría ser más barato y más fácil que cualquier otra alternativa utilizando discreto lógica. Por cierto, las instrucciones clrwdt podrían moverse a la subrutina, pero hacerlo haría las cosas menos sólidas. Como está escrito, incluso si una falla produce que la pila de direcciones de retorno se dañe, el perro guardián no se alimentará hasta que la ejecución vuelva al bucle principal. Si eso nunca sucede, el controlador reiniciará el chip después de un par de segundos.

    
respondido por el supercat
26

¿"SOLO" 384 bytes?

Hace mucho tiempo, tuve la tarea de escribir un sistema operativo completo (por mi cuenta) para una computadora especializada que prestaba servicio a la industria de gestión de refinerías, tuberías y oleoductos. El primer producto de este tipo de la compañía estaba basado en 6800 y se estaba actualizando a 6809, y querían que un nuevo sistema operativo fuera compatible con el 6809 para poder eliminar los costos de licencia del sistema operativo original. También estaban aumentando el tamaño de la rom de arranque a 64 bytes, en comparación con 32. Si recuerdo bien, ¡fue hace aproximadamente 33 años! - Convencí a los ingenieros para que me dieran 128 bytes para poder poner los controladores de dispositivo de todo el sistema operativo en la rom y así hacer que todo el dispositivo sea más confiable y versátil. Esto incluía:

  • Controlador de teclado con rebote clave
  • controlador de video
  • Controlador de la unidad de disco y sistema de archivos rudimentario ("abloader format" de Motorola, IIRC), con capacidad incorporada para tratar la memoria "acumulada" como si fuera un disco de disco realmente rápido.
  • Controlador de módem (obtuvieron el FSK al revés, por lo que estos módems solo hablaron entre sí)

Sí, todos estos eran tan simples como se veían, y estaban optimizados a mano para eliminar todos los ciclos extraños, pero perfectamente reparables y confiables. Sí, he puesto todo eso en los bytes disponibles: oh, TAMBIÉN configuró el manejo de interrupciones, las diferentes pilas, e inicialicé el sistema operativo en tiempo real / multitarea, le indiqué al usuario las opciones de arranque y arranqué el sistema.

Un amigo mío que todavía está afiliado a la compañía (su sucesor) me dijo hace unos años que mi código todavía está en servicio.

Puedes hacer mucho con 384 bytes ...

    
respondido por el Richard T
21

Puede usar esto para aplicaciones muy pequeñas (por ejemplo, inicio de PSU retrasado , 555 reemplazo del temporizador , control basado en triac , LED parpadeando, etc. ... con una huella más pequeña de la que necesitaría con puertas lógicas o un temporizador 555.

    
respondido por el Renan
17

Diseñé un sensor de humedad para plantas que rastrea la cantidad de agua que tiene la planta y parpadea un LED si la planta necesita agua. Puede hacer que el sensor aprenda el tipo de planta y, por lo tanto, cambie su configuración mientras se ejecuta. Detecta bajo voltaje en la batería. Me quedé sin flash y memoria RAM, pero pude escribir todo en código C para hacer que este producto funcionara perfectamente.

Usé la foto10f que mencionas.

Aquí está el código que hice para el sensor de agua de mi planta. Utilicé pic10f220 ya que tiene un módulo ADC, tiene la misma memoria que la pic10f200, intentaré encontrar el esquema mañana.

El código está en español, pero es muy simple y debe entenderse fácilmente. Cuando el Pic10F salga del modo de suspensión, se reiniciará, por lo que debe verificar si fue un PowerUp o un reinicio y actuar en consecuencia. La configuración de la planta se mantiene en ram, ya que nunca se apaga realmente.

MAIN.C

/*
Author: woziX (AML)

Feel free to use the code as you wish. 
*/

#include "main.h"

void main(void) 
{  
    unsigned char Humedad_Ref;
    unsigned char Ciclos;
    unsigned char Bateria_Baja;
    unsigned char Humedad_Ref_Bkp;

    OSCCAL &= 0xfe;             //Solo borramos el primer bit
    WDT_POST64();                   //1s
    ADCON0 = 0b01000000;
    LEDOFF();
    TRIS_LEDOFF(); 

    for(;;) 
    {  
        //Se checa si es la primera vez que arranca
        if(FIRST_RUN())
        {
            Ciclos = 0;
            Humedad_Ref = 0;
            Bateria_Baja = 0;
        }

        //Checamos el nivel de la bateria cuando arranca por primera vez y cada 255 ciclos.
        if(Ciclos == 0)
        {
            if(Bateria_Baja)
            {
                Bateria_Baja--;
                Blink(2);
                WDT_POST128();
                SLEEP();
            }       

            if(BateriaBaja())
            {
                Bateria_Baja = 100;     //Vamos a parpadear doble por 100 ciclos de 2 segundos
                SLEEP();
            }
            Ciclos = 255;
        }   

        //Checamos si el boton esta picado
        if(Boton_Picado)
        {
            WDT_POST128();
            CLRWDT();
            TRIS_LEDON(); 
            LEDON();
            __delay_ms(1000);   
            TRIS_ADOFF();
            Humedad_Ref = Humedad();
            Humedad_Ref_Bkp = Humedad_Ref;
        }   

        //Checamos si esta calibrado. Esta calibrado si Humedad_Ref es mayor a cero
        if( (!Humedad_Ref) || (Humedad_Ref != Humedad_Ref_Bkp) )
        {
            //No esta calibrado, hacer blink y dormir
            Blink(3);
            SLEEP();
        }   

        //Checamos que Humedad_Ref sea mayor o igual a 4 antes de restarle 
        if(Humedad_Ref <= (255 - Offset_Muy_Seca))
        {
            if(Humedad() > (Humedad_Ref + Offset_Muy_Seca)) //planta casi seca
            {
                Blink(1);
                WDT_POST32();
                SLEEP();    
            }       
        }

        if(Humedad() >= (Humedad_Ref))  //planta seca
        {
            Blink(1);
            WDT_POST64();
            SLEEP();    
        }   

        if(Humedad_Ref >= Offset_Casi_Seca )
        {
            //Si Humedad_Ref es menor a Humedad, entonces la tierra esta seca. 
            if(Humedad() > (Humedad_Ref - Offset_Casi_Seca))  //Planta muy seca
            {
                Blink(1);
                WDT_POST128();
                SLEEP();    
            }
        }

        SLEEP();
    }  
} 

unsigned char Humedad (void)
{
    LEDOFF();
    TRIS_ADON();
    ADON();
    ADCON0_CH0_ADON();
    __delay_us(12); 
    GO_nDONE = 1;
    while(GO_nDONE);
    TRIS_ADOFF();
    ADCON0_CH0_ADOFF();
    return ADRES;
}   

//Regresa 1 si la bateria esta baja (fijado por el define LOWBAT)
//Regresa 0 si la bateria no esta baja
unsigned char BateriaBaja (void)
{
    LEDON();                
    TRIS_ADLEDON();
    ADON();
    ADCON0_ABSREF_ADON();
    __delay_us(150);        //Delay largo para que se baje el voltaje de la bateria 
    GO_nDONE = 1;
    while(GO_nDONE);
    TRIS_ADOFF();
    LEDOFF();
    ADCON0_ABSREF_ADOFF();  
    return (ADRES > LOWBAT ? 1 : 0);
}   

void Blink(unsigned char veces)
{
    while(veces)
    {
        veces--;
        WDT_POST64();
        TRIS_LEDON(); 
        CLRWDT();
        LEDON();
        __delay_ms(18); 
        LEDOFF();
        TRIS_ADOFF();
        if(veces)__delay_ms(320);   
    }   
}   

MAIN.H

/*
Author: woziX (AML)

Feel free to use the code as you wish. 
*/

#ifndef MAIN_H
#define MAIN_H

#include <htc.h>
#include <pic.h>

 __CONFIG (MCPU_OFF  & WDTE_ON & CP_OFF & MCLRE_OFF & IOSCFS_4MHZ ); 

#define _XTAL_FREQ              4000000
#define TRIS_ADON()             TRIS = 0b1101
#define TRIS_ADOFF()            TRIS = 0b1111
#define TRIS_LEDON()            TRIS = 0b1011
#define TRIS_LEDOFF()           TRIS = 0b1111
#define TRIS_ADLEDON()          TRIS = 0b1001


#define ADCON0_CH0_ADON()          ADCON0 = 0b01000001;     // Canal 0 sin ADON
#define ADCON0_CH0_ADOFF()       ADCON0 = 0b01000000;       // Canal 0 con adON
#define ADCON0_ABSREF_ADOFF()    ADCON0 = 0b01001100;       //Referencia interna absoluta sin ADON
#define ADCON0_ABSREF_ADON()     ADCON0 = 0b01001101;       //referencia interna absoluta con ADON

//Llamar a WDT_POST() tambien cambia las otras configuracion de OPTION
#define WDT_POST1()   OPTION = 0b11001000
#define WDT_POST2()   OPTION = 0b11001001
#define WDT_POST4()   OPTION = 0b11001010
#define WDT_POST8()   OPTION = 0b11001011
#define WDT_POST16()  OPTION = 0b11001100
#define WDT_POST32()  OPTION = 0b11001101
#define WDT_POST64()  OPTION = 0b11001110
#define WDT_POST128() OPTION = 0b11001111

#define Boton_Picado    !GP3
#define FIRST_RUN()     (STATUS & 0x10) //Solo tomamos el bit TO

//Offsets
#define Offset_Casi_Seca  5
#define Offset_Muy_Seca   5

 //Low Bat Threshold
#define LOWBAT                    73
/*
Los siguientes valores son aproximados
LOWBAT  VDD
50      3.07
51      3.01
52      2.95
53      2.90
54      2.84
55      2.79
56      2.74
57      2.69
58      2.65
59      2.60
60      2.56
61      2.52
62      2.48
63      2.44
64      2.40
65      2.36
66      2.33
67      2.29
68      2.26
69      2.23
70      2.19
71      2.16
72      2.13
73      2.10
74      2.08
75      2.05
76      2.02
77      1.99
78      1.97
*/


#define LEDON()                 GP2 = 0; //GPIO = GPIO & 0b1011
#define LEDOFF()                GP2 = 1; //GPIO = GPIO | 0b0100
#define ADON()                  GP1 = 0; //GPIO = GPIO & 0b1101
#define ADOFF()                 GP1 = 1; //GPIO = GPIO | 0b0010

unsigned char Humedad (void);
unsigned char BateriaBaja (void);
void Delay_Parpadeo(void);
void Blink(unsigned char veces);

#endif

Hazme saber si tienes preguntas, intentaré responder de acuerdo con lo que recuerdo. Codifiqué esto hace varios años, así que no compruebas mis habilidades de codificación, han mejorado :).

Nota Final. Utilicé el compilador Hi-Tech C.

    
respondido por el scrafy
16

Una cosa que no he visto mencionada: el microcontrolador que mencionó es de solo $ 0.34 cada uno en cantidades de 100. Por lo tanto, para productos baratos producidos en serie, puede tener sentido ir al problema de codificación adicional impuesto por tal unidad limitada. Lo mismo podría aplicarse al tamaño o al consumo de energía.

    
respondido por el Mark Harrison
14

Cuando estaba en la escuela secundaria, tuve un profesor que insistía en que la atenuación ligera era una tarea demasiado difícil para un estudiante como yo para abordar.

Así desafiado, pasé bastante tiempo aprendiendo y comprendiendo la regulación de la luz basada en la fase usando triacs, y programando el 16C84 desde el microchip para realizar esta hazaña. Terminé con este código de ensamblaje:

'Timing info:
'There are 120 half-cycles in a 60Hz AC waveform
'We want to be able to trigger a triac at any of 256 
'points inside each half-cycle.  So:
'1 Half cycle takes 8 1/3 mS
'1/256 of one half cycle takes about 32.6uS
'The Pause function here waits (34 * 0xD)uS, plus 3uS overhead
'Overhead includes CALL PAUSE.
'This was originally assembled using Parallax's "8051 style" 
'assembler, and was not optimized any further.  I suppose
'it could be modified to be closer to 32 or 33uS, but it is
'sufficient for my testing purposes.

list 16c84

    movlw   0xFD     '11111101
    tris    0x5      'Port A
    movlw   0xFF     '11111111
    tris    0x6      'Port B
WaitLow:             'Wait for zero-crossing start
    btfss   0x5,0x0  'Port A, Bit 1
    goto    WaitLow  'If high, goto WaitLow
WaitHigh:            'Wait for end of Zero Crossing
    btfsc   0x5,0x0  'Port A, Bit 1
    goto    WaitHigh 'If low, goto waitHigh
    call    Pause    'Wait for 0xD * 34 + 3 uS
    bcf     0x5,0x1  'Put Low on port A, Bit 1
    movlw   0x3      'Put 3 into W
    movwf   0xD      'Put W into 0xD
    call    Pause    'Call Pause, 105 uS
    bsf     0x5,0x1  'Put High on Port A, Bit 1
    decf    0xE      'Decrement E
    movf    0x6,W    'Copy Port B to W
    movwf   0xD      'Copy W to 0xD
    goto    Start    'Wait for zero Crossing
Pause:               'This pauses for 0xD * 34 + 3 Micro Seconds
                     'Our goal is approx. 32 uS per 0xD
                     'But this is close enough for testing
    movlw   0xA      'Move 10 to W
    movwf   0xC      'Move W to 0xC
Label1:
    decfsz  0xC      'Decrement C
    goto    Label1   'If C is not zero, goto Label1
    decfsz  0xD      'Decrement D
    goto    Pause    'If D is not zero, goto Pause
    return           'Return

Por supuesto que necesitarías modificar esto para el chip que mencionas, y tal vez agregar una rutina serial barata para la entrada, ya que tu chip no tiene un puerto de 8 bits de ancho para escuchar, pero la idea es que aparentemente El trabajo complejo se puede hacer en muy poco código: puede colocar diez copias del programa anterior en el 10F200.

Puede encontrar más información sobre el proyecto en mi página Light Dimming . Por cierto, nunca le mostré esto a mi profesor, pero terminé haciendo varias plataformas de iluminación para mi amigo DJ.

    
respondido por el Adam Davis
12

Hace años, escribí un controlador de temperatura con E / S en serie (golpeando los bits de E / S en serie porque la MCU no tenía un UART) y un simple intérprete de comandos para hablar con el controlador. MCU era un MC68HC705K1 de Motorola (ahora Freescale) que tenía 504 bytes de memoria de programa (OTPROM) y unos 32 bytes de RAM. No es tan pequeño como el PIC al que hace referencia, pero recuerdo que me sobra algo de ROM. Todavía me quedan unas pocas unidades ensambladas, 17 años después; quieres comprar uno?

Así que sí, se puede hacer, al menos en ensamblaje.

En cualquier caso, recientemente he escrito programas en C muy simples que probablemente encajarían dentro de 384 bytes cuando estaban optimizados. No todo requiere software grande y complejo.

    
respondido por el lyndon
5

Puede escribir un parpadeo de un LED con una memoria de programa de 384 bytes, e incluso más.

Por lo que sé, no es posible ampliar la memoria del programa con un chip externo (a menos que esté creando un intérprete completo de ASM en los 384 bytes , que sería lento). Sin embargo, es posible ampliar la memoria de datos con un chip externo (EEPROM, SRAM).

    
respondido por el Keelan
4

En realidad es peor de lo que crees. Su página Mouser vinculada es un asunto confuso cuando especifica que este procesador tiene 384 bytes de memoria de programa. El PIC10F200 en realidad tiene 256 palabras de 12 bits de la memoria del programa.

Entonces, ¿qué puedes hacer con eso? El conjunto de instrucciones PIC de 12 bits utilizado por los dispositivos PIC10F20 x Todas son instrucciones de una sola palabra, por lo que después de restar algunas instrucciones para la configuración del procesador, queda suficiente espacio para un programa de aproximadamente 250 pasos. Eso es suficiente para muchas aplicaciones. Probablemente podría escribir un controlador de lavadora en ese tipo de espacio, por ejemplo.

Acabo de revisar los compiladores PIC C disponibles, y parece que aproximadamente la mitad de ellos ni siquiera intentarán emitir código para un PIC10F200. Es probable que aquellos que publican tanto código de repetición que solo puedas escribir un indicador de luz LED en el espacio que queda. Realmente desea utilizar el lenguaje ensamblador con un procesador de este tipo.

    
respondido por el Warren Young
0

agitando mi bastón en mi día, ¡tuvimos que grabar nuestros propios pedazos de arena!

En 1976 (o más o menos) el sistema Atari 2600 VCS fue una de las "plataformas de videojuegos" más populares de la época. En él, el microprocesador (MOSTEK 6507) funcionó a una velocidad de ~ 1 MHz y tenía **** 128 bytes de RAM **.

Un segundo ejemplo que recuerdo de un microcontrolador con RAM extremadamente limitada (~ 128 bytes) fue un PIC12F utilizado en un convertidor DC-DC. Este micro también tuvo que usar lenguaje ensamblador para funcionar.

    
respondido por el cowboydan

Lea otras preguntas en las etiquetas