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

Por ejemplo un PIC10F200T

Prácticamente cualquier código que escriba será más grande que eso, a menos que sea un chip de un solo propósito. ¿Hay alguna forma de cargar más memoria de programa desde un almacenamiento externo o algo así? Solo tengo curiosidad, no veo cómo esto podría ser muy útil... pero debe serlo.

Hay muchas aplicaciones para los microcontroladores diminutos, desde generadores de señales especiales hasta convertidores de protocolos, "nodos" en un sistema de control más grande, etc., etc.
Algunas tareas son muy sencillas, como tomar una decisión en base a una prueba o configurar algún chip de función. Casi cualquier cosa puede cargar una lista de tareas pendientes desde un almacenamiento externo, aunque la versatilidad de hacerlo tiende a estar limitada por la memoria RAM interna para el seguimiento del estado, a menos que también se use una memoria RAM externa. Pero eso casi nunca es económico con un dispositivo como este, en comparación con un procesador con más recursos en el chip.
Bueno, un programa para jugar al ajedrez ocupa 672 bytes, así que eso no es bueno. en.wikipedia.org/wiki/1K_ZX_Chess
Estos son algunos ejemplos de lo que se puede hacer con programas pequeños (menos de 256 bytes).
¿Qué quieres decir con "a menos que sea un chip de un solo propósito"? La mayoría de los sistemas integrados son de un solo propósito.
@JohnBurton ¿Los 672 bytes de ese programa de ajedrez son completamente autónomos y no hacen absolutamente ninguna llamada a ninguna rutina de soporte que ya esté en la computadora?
Con montaje o un Forth interactivo muy pequeño no necesitas mucho espacio.
Qué quieres por 36 centavos (cantidad 100, en cinta).
De vuelta en la universidad, construí un programa de semáforo completamente funcional para una computadora 8085/8155 (máximo 256 bytes) que ensamblé. Tenía botones para caminar y algunos sensores que simularían la presencia de un vehículo.
@JeannePindar Exactamente lo que dije es lo que quise decir. Piénselo, si está construyendo un automóvil RC, entonces el microcontrolador decodificará las señales de radio entrantes, será responsable de impulsar los motores de las ruedas y será responsable de que los servos giren. las ruedas. Un chip de un solo propósito, por otro lado, solo hace una tarea. Podría servir como multiplexor, podría servir como FPU, cualquier otra cosa.
Para la mayoría de los desarrolladores de microcontroladores, creo que "ejecutar un automóvil RC" sería "un solo propósito", y creo que un buen programador de lenguaje ensamblador podría acercarse a los 384 bytes para esa aplicación.
@JohnBurton: El enlace 1K ZX Chess dice que usa 672 bytes de RAM y no menciona el uso de ROM. La pregunta es sobre 384 bytes de memoria de programa .
@AndreyVihrov El programa se ejecuta desde la RAM, por lo que incluye tanto el programa como la memoria RAM de trabajo. De todos modos, no fue una publicación muy seria, más solo un ejemplo de lo que puede hacer en entornos con recursos muy restringidos si tiene la necesidad y la motivación.
@ coder543 aunque no tengo estadísticas al respecto, muchos microcontroladores simplemente reemplazan los circuitos integrados de un solo propósito, como multiplexores, temporizadores, puertas lógicas, etc.
Podría escribir un programa x86 que se use int 13hpara borrar su disco duro en menos de 0x1BE (446) bytes. ¿Cuenta mi programa de sector de arranque para x86?
384 bytes es más que suficiente para la mayoría de las aplicaciones de microcontroladores. La mayoría de los microcontroladores utilizan una "arquitectura de Harvard", lo que significa que hay una memoria separada para las instrucciones y el núcleo de la CPU puede acceder a la memoria de instrucciones y a la memoria de datos al mismo tiempo. 64 bytes suele ser más que suficiente, 384 bytes suele ser excesivo.
Una de las primeras competencias de robótica para estudiantes aquí tiene un límite de tamaño de código de 256 bytes. No se permiten redes de microcontroladores. Compro chips de microcontrolador con 8K Flash simplemente porque cuestan prácticamente lo mismo que las variantes de 2K y tengo cosas basadas en RTOS que requieren 6K Flash.
Me gustaría ver la explicación en los foros de Arduino. :-))

Respuestas (11)

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

384 bytes es mucho espacio para crear algo bastante complejo en ensamblador.

Si rebuscas en la historia hasta cuando las computadoras tenían el tamaño de una habitación, encontrarás algunas hazañas artísticas realmente asombrosas 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 para jugar, los infieles decadentes.

También mire algunas de las viejas competencias de demostración donde el desafío era colocar una "introducción" en el bloque de arranque de un disquete, los objetivos típicos eran 4k o 40k y generalmente lograban incluir música y animación.

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

Editar para los jóvenes:

  • Disquete = disquete.
  • Bootblock = 1er sector del disquete leído en el arranque.
  • Demoscene = concursos de programación entre grupos de hackers.
  • Ensamblador = forma elegante de programar un dispositivo si es demasiado suave para usar 8 interruptores de palanca y un botón de "almacenamiento".
La consola de juegos Atari 2600 tenía solo 4 KB de almacenamiento en los cartuchos de juegos ROM (aunque algunos juegos superaron esta limitación al usar el cambio de banco para acceder a más de 4K).
Hace eones hice un canto de pájaro bastante realista (lo suficiente como para que la gente buscara al pájaro en lugar de sospechar de la computadora), cuyas tripas (pero no el código aleatorio que impedía que hiciera exactamente el mismo sonido cada vez) se habría sacudido alrededor de 384 bytes y tenía las restricciones adicionales de direcciones no escribibles y no se permitía un byte cero en el binario.
Necesito salir más, recordé esto de antes: protector de pantalla en 368 bytes: aminet.net/package/util/blank/368blanker
+1 para "La historia de Mel". Una de las mejores cosas que he leído en toda la semana.
@JohnU: Los primeros juegos en Atari 2600 fueron todos 2K. Muchos desarrolladores nunca diseñaron ningún juego que fuera más allá de 4K, porque a pesar de que los chips de 8K eran asequibles (y los carritos de algunas empresas simplemente usaban la mitad de un chip de 4K), agregar el cambio de banco a una tarjeta usando un estándar (selección de chip activo bajo) chip aumentó el número de chips de soporte de uno a tres.
@JohnU: No he examinado un cartucho de Tigervision, aunque por lo que entiendo de su esquema bancario, podrían haber reducido la cantidad de chips a dos, para admitir hasta 1024K. En 2006, diseñé un cartucho de 8K usando solo una parte de 74LS, una EPROM, una resistencia y una tapa (junto con una tapa de derivación). Me pregunto si otras empresas habrían fabricado cartuchos de 8K si mi circuito se hubiera conocido 'en el pasado'.
Asocié mi cuenta solo para votar a favor de 'La historia de Mel'.
Gracias por La historia de Mel . Me hizo reír y nostálgico al mismo tiempo :)
Un enlace que me perdí, con algo de codificación real: blog.jgc.org/2013/04/how-i-coded-in-1985.html
Uno tardío pero alguien administró el trazado de rayos en 256 bytes: tech.slashdot.org/story/13/05/15/1913222/…
Oye, tenía 12 interruptores de palanca y un interruptor de "depósito". ¿Eso cuenta? Tuvimos que alternar manualmente el cargador de arranque que leería el resto del sistema operativo de la cinta de papel. La mejor parte fue que el maestro pensó que arriba era 0 y abajo era 1, así que me las arreglaba para los choques mientras estaba en la clase de inglés.
Tentado a menos-1 solo porque desperdicié [redactado] horas recordando el archivo de jerga. A veces echo de menos los (¿buenos?) viejos tiempos.
Acabo de compilar un código PIC (muy perezoso, usando bibliotecas estándar) en MikroeC PRO, lee una matriz de teclado 4x4 y la escribe en los pines IO como un byte de datos... tamaño de código 238 bytes.
Otra aventura en código pequeño: un ejecutable de 45 bytes: muppetlabs.com/~breadbox/software/tiny/teensy.html
Puedes jugar al ajedrez con unos pocos bytes de sobra: hackaday.com/2019/03/28/write-a-very-tiny-chess-program aunque tienes que estirar hasta 477 si quieres mejores oponentes de IA;)
Vote a favor por su excelente definición de ensamblador. :)

Los microcontroladores son lo suficientemente baratos como para que a menudo se usen para hacer cosas realmente simples que en el pasado probablemente se hubieran hecho con lógica discreta. Cosas realmente simples. Por ejemplo, uno podría querer que un dispositivo encienda una salida durante un segundo cada cinco segundos, con más precisión de lo que sería capaz de hacer un temporizador 555.

  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 y 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 todavía tenga mucho espacio de sobra, pero incluso si todo lo que hiciera el chip fuera precisamente lo que se muestra arriba, aún podría ser más barato y más fácil que cualquier alternativa usando discreto lógica. Por cierto, las clrwdtinstrucciones podrían moverse a la subrutina, pero hacerlo haría que las cosas fueran menos sólidas. Tal como está escrito, incluso si una falla hace que la pila de direcciones de retorno se corrompa, el perro guardián no se alimentará hasta que la ejecución regrese al bucle principal. Si eso nunca sucede, el perro guardián restablecerá el chip después de un par de segundos.

¡Gracias! esto es casi exactamente lo que estaba buscando. Ahora entiendo mejor cómo son útiles.
@ coder543 esta es una buena respuesta, pero no la acepte tan rápido. Eso anima a otros a responder, ¡mientras que podría haber mejores respuestas por ahí! Solo espera uno o dos días, para que todas las zonas horarias hayan tenido una oportunidad.
bien, por cierto, ¿cómo funciona esa función Wait1Sec? Mirándolo, parece estar colocando un conjunto interesante de números en tres registros, luego haciendo un bucle hasta que son cero. Supongo que ese es el número mágico que tardará 1 segundo en disminuir a cero.
Utiliza efectivamente tres bytes de memoria como un contador de 24 bits, que se carga para que se repita unas 333.330 veces. Dado que el bucle interno se ejecuta una vez cada tres ciclos y el PIC probablemente se ejecuta 1.000.000 de ciclos/seg, eso debería tomar alrededor de un segundo. La primera vez que se alcance el bucle interior se ejecutará 17 veces. Después de eso, count0será cero, por lo que las veces posteriores que se alcance el bucle interno se ejecutarán 256 veces. Adiviné los números a usar; si fuera un proyecto real, los habría calculado con más precisión. En cualquier caso, si la memoria sirve, el PIC tiene 16 bytes de RAM; Estoy usando menos del 20%.
Honestamente, podría optimizar su código un poco, dando un mal ejemplo a los niños: ¿¿5 llamadas separadas para esperar 1 segundo? ¡Derrochador! ;)
@JohnU: FYI, el código usa llamadas separadas porque si usara un contador de conteo hasta cero y el conteo fallara, el ciclo podría ejecutarse 255 veces en lugar de cuatro, mientras alimenta al perro guardián una vez por segundo. Si bien sería posible protegerse contra eso verificando en cada ciclo si el conteo estaba dentro del rango, el código para hacerlo termina siendo más complicado que cinco llamadas y cinco clrwdtinstrucciones. Este no es el arreglo de contador más absolutamente seguro contra fallas posible, pero se le da cierta consideración a los problemas de seguridad (por ejemplo, la evitación de clrwdtdentro de una subrutina).
¿Con qué frecuencia ocurren realmente estos fallos?
@coder543: En ausencia de cosas como el ruido de la fuente de alimentación, no mucho. Por otro lado, en partes sin un detector de caída de voltaje, es posible que sucedan todo tipo de cosas locas si VDD cae a un nivel entre el voltaje mínimo de operación y tierra, y luego vuelve a subir a la normalidad. En general, se debe tratar de garantizar que cualquier estado en el que se encuentre el dispositivo volverá a la normalidad en un período de tiempo razonable. Los dos segundos más o menos para que el perro guardián se active pueden ser inevitables, pero cuatro minutos para que un contador fallido llegue a cero puede ser demasiado.
@coder543, ocurren más a menudo en una demostración importante de lo que quieres creer. Este tipo de pensamiento también se requiere cuando se construyen cosas profundamente integradas que no tienen medios para pedir ayuda o informar un error. O son inaccesibles (piense en el mar profundo o en el espacio exterior) incluso si se notó un error.
@supercat - Espero que hayas notado el emoticón en mi comentario. Amando tu trabajo.
@JohnU: Lo noté, pero pensé que explicar por qué escribí el código como lo hice podría ser útil. Por cierto, también estaba tratando de mostrar que las tareas pequeñas pueden caber en un procesador pequeño incluso si no están optimizadas de manera absolutamente perfecta.

¿"SÓLO" 384 bytes?

Hace mucho tiempo, tuve el trabajo de escribir un sistema operativo completo (por mí mismo) para una computadora especializada que servía a la industria de administración de barcos, oleoductos y refinerías. El primer producto de este tipo de la compañía estaba basado en 6800 y se estaba actualizando a 6809, y querían un nuevo sistema operativo que acompañara al 6809 para poder eliminar los costos de licencia del sistema operativo original. También aumentaron el tamaño de la rom de arranque a 64 bytes, en lugar de 32. Si no recuerdo mal, ¡FUE hace unos 33 años! - Convencí a los ingenieros para que me dieran 128 bytes para poder poner todos los drivers del dispositivo del sistema operativo en la rom y así hacer todo el dispositivo más fiable y versátil. Esto incluía:

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

Sí, todos estos fueron tan básicos como parece, y optimizados a mano para eliminar todos los ciclos extraños, pero perfectamente reparables y confiables. Sí, calcé todo eso en los bytes disponibles; oh, TAMBIÉN configuró el manejo de interrupciones, las diversas pilas e inicializó el sistema operativo en tiempo real / multitarea, le indicó al usuario las opciones de arranque y arrancó el sistema.

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

Puedes hacer MUCHO con 384 bytes...

dice boot rom, y menciona mover los controladores a la boot rom... esto me indica que había un medio de almacenamiento secundario disponible. En esta discusión, ya determinamos que no puede cargar código desde un almacenamiento externo en este PIC.
@ coder543 Eso no entiende el punto: ¡384 bytes son suficientes para hacer bastante! La pregunta original se leía como una queja de que 384 no era suficiente para hacer nada útil, era más de lo que necesitaba, MUCHO más, para proporcionar todos los componentes fundamentales de un sistema operativo multitarea en tiempo real...
Ese es el tipo de proyecto en el que me encantaría trabajar y, a menudo, desearía estar presente en esa era de la informática para poder experimentar realmente la emoción de hacer algo tan poderoso con tan poco. No me malinterpreten, creo que es increíble lo que la tecnología ha permitido hoy en día, y es increíble que las personas puedan crear programas complejos sin problemas de memoria, pero personalmente disfruto de las máquinas más limitadas de antaño. Y es bueno que fueran lo suficientemente simples como para que una sola persona pudiera encajar todo el funcionamiento de su computadora en su cabeza. Ya no puedo hacer eso...

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

¡Acabo de darme cuenta de que esos dos primeros ejemplos eran del propio Stack! bien jugado.
+1 por mencionar la huella. A veces el tamaño lo es todo.

Una cosa que no he visto mencionada: el microcontrolador que mencionó cuesta solo $ 0.34 cada uno en cantidades de 100. Entonces, para productos baratos producidos en masa, puede tener sentido pasar por el problema de codificación adicional impuesto por una unidad tan limitada. Lo mismo podría aplicarse al tamaño o al consumo de energía.

Ese fue exactamente mi primer pensamiento. Además: si fuera una empresa nueva con una buena idea, pero solo unos pocos cientos de dólares perdidos, cosas como esta pueden significar la diferencia entre volver al trabajo diario y dejar el trabajo diario.

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 ram, pero pude escribir todo en código C para que este producto funcionara sin problemas.

Usé el pic10f que mencionas.


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

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

C PRINCIPAL

/*
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);   
    }   
}   

PRINCIPAL.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

Avíseme si tiene preguntas, intentaré responder según lo que recuerdo. Codifiqué esto hace varios años, así que no revises mis habilidades de codificación, han mejorado :).

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

De hecho, sería muy interesante leer cómo hiciste esto. ¿Tomaste alguna nota mientras lo hacías que no te importaría compartir en las webs?
Hola RhysW, creo que todavía tengo el código. En realidad fue muy simple. Podría enviarte mi código si estás interesado. Hágamelo saber. El circuito que diseñé es muy simple y genial, solo 3 resistencias, un mosfet de canal p (para protección de batería inversa), una tapa de 100nF y un LED. Uso un diodo interno en el pic10f para usar como referencia para medir la batería y mantener constantes las lecturas de ADC.
Eso suena como un proyecto limpio. ¿Hay alguna posibilidad de que pueda publicar los detalles aquí (o al menos publicarlos en algún lugar y vincularlos)?
Hola scrafy! Por favor, si tiene algo que agregar a una respuesta, use el enlace "editar" en lugar de publicar una nueva respuesta, ya que este sitio utiliza la votación y no funciona como un foro.

Cuando estaba en la escuela secundaria, tuve un maestro que insistía en que atenuar la luz era una tarea demasiado difícil de abordar para un estudiante como yo.

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

'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, necesitaría modificar esto para el chip que menciona, y tal vez agregar una rutina serial barata para la entrada ya que su chip no tiene un puerto de 8 bits de ancho para escuchar, pero la idea es que un trabajo aparentemente complejo puede hacerse con 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 de atenuación de luz . Por cierto, nunca le mostré esto a mi maestro, pero terminé haciendo una serie de equipos de iluminación para mi amigo DJ.

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

Entonces sí, se puede hacer, al menos en ensamblaje.

En cualquier caso, recientemente he escrito programas en C muy simples que probablemente habrían cabido dentro de 384 bytes cuando se optimizaron. No todo requiere un software grande y complejo.

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

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

No sería difícil construir un simulador de máquina de Turing en 384 bytes...
@ChrisStratton Quise decir un intérprete completo, de modo que la 'memoria de programa extendida' tuviera las mismas características que de costumbre.
Sí, eso es lo que sugerí como un medio de implementación estricta. El resto es solo diseño del compilador...
@ChrisStratton Supongo que tienes razón, eliminé la parte que no es posible.
Sí, es posible, pero simplemente inútil y lento, excepto como curiosidad académica o para aplicaciones extremadamente esotéricas.
No poder cargar código directamente es decepcionante, pero tiene sentido. Y sí, siempre se podría agregar un intérprete subleq.
Si uno quisiera que la lógica del programa se almacenara en una EEPROM externa, tratar de emular el conjunto de instrucciones PIC no sería el camino a seguir. Un mejor enfoque sería diseñar un conjunto de instrucciones optimizado para su uso con la máquina virtual; de hecho, ese es el enfoque que tomó Parallax con su "STAMP básico" en la década de 1990. Era un PIC con 3072 bytes de espacio de código, emparejado con un chip EEPROM en serie.
Por cierto, una nota adicional sobre el sello BASIC: se introdujo en un momento en que los microcontroladores basados ​​en flash o EEPROM eran comparativamente raros, pero los chips EEPROM en serie eran bastante baratos. Para aplicaciones que no necesitaban mucha velocidad, un micro de código fijo con una parte de EEPROM en serie sería más económico que una EEPROM de tamaño similar o un micro basado en flash. El diseño del BASIC Stamp no tendría sentido hoy en día, pero fue bastante práctico cuando se presentó.
De hecho, un intérprete sería un poco lento, pero utilizable. Incluso hay disponible una implementación del emulador ARM de 32 bits con MMU en un microcontrolador de 8 bits . Un emulador de 8 bits con un conjunto de instrucciones optimizado definitivamente sería mucho más rápido
@LưuVĩnhPhúc sí, pero ese proyecto usa más de 384 bytes de memoria de programa si no me equivoco.
Sí, porque debe implementar matemáticas de 32 bits y el conjunto completo de instrucciones ARM + golpe de bits para SPI + MMU... por lo que, por supuesto, necesita mucha memoria de programa. Pero para un conjunto de instrucciones personalizado simple, 384 bytes es más que suficiente
Yo sé eso. Solo estoy señalando por qué su comentario no es realmente relevante para esta pregunta.

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

Entonces, ¿qué puedes hacer con eso? El conjunto de instrucciones PIC de 12 bits utilizado por los dispositivos PIC10F20 x son todas 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.

Revisé los compiladores PIC C disponibles y parece que la mitad de ellos ni siquiera intentarán emitir código para un PIC10F200. Aquellos que probablemente emitan tanto código repetitivo que es posible que solo pueda escribir una luz intermitente LED en el espacio que queda. Realmente desea utilizar el lenguaje ensamblador con dicho procesador.

Tienes razón sobre las 256 palabras de instrucción. En realidad, uno de ellos se toma con la constante de calibración del oscilador, por lo que obtienes 255 instrucciones útiles. Además, el 10F200 no utiliza el conjunto de instrucciones de 14 bits PIC 16 habitual. Utiliza el conjunto de instrucciones PIC 12 de 12 bits. Sin embargo, estoy de acuerdo con sus premisas básicas. He hecho muchas cosas útiles con un PIC 10F200. +1
@OlinLathrop: He aclarado la respuesta. Obtuve el término PIC16 de la página 51 de la hoja de datos , pero decidí que es más claro referirme simplemente al "conjunto de instrucciones de 12 bits". El prefijo de parte no es una guía confiable para el conjunto de instrucciones utilizado.

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

En 1976 (más o menos) el sistema Atari 2600 VCS era una de las "plataformas de videojuegos" más populares de la época. En él, el microprocesador (MOSTEK 6507) funcionaba a una increíble 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 poder ejecutarse.

El OP no está hablando de RAM, está hablando de espacio de programa. El espacio del programa en el Atari 2600 está en el cartucho, no en el chip RIOT . El 2600 admite ROM de programa de hasta 4 kiB sin cambio de banco. (¡Y algunos cartuchos comerciales cambiaron de banco!) En cuanto a su ejemplo PIC12F, el OP lo ganó: los dispositivos de la serie PIC10F20x tienen 16 o 24 bytes de SRAM.