# How can anyone use a microcontroller which has only 384 bytes of program memory?

For instance a PIC10F200T

Virtually any code you write will be larger than that, unless it is a single purpose chip. Is there any way to load more program memory from external storage or something? I'm just curious, I don't see how this could be very useful... but it must be.

• There are plenty of applications for tiny microcontrollers, from special-purpose signal generators, to protocol converters, to "nodes" in a larger control system, etc., etc., May 1 '13 at 15:17
• Well a chess playing program takes 672 bytes so that's no good. en.wikipedia.org/wiki/1K_ZX_Chess May 1 '13 at 16:41
• Here are some examples of what can be done with tiny programs (less than 256 bytes). May 1 '13 at 19:37
• What do you mean, "unless its a single purpose chip"? The majority of embedded systems are single purpose. May 1 '13 at 20:25
• Back in college, I built a fully functional traffic light program for a 8085/8155 computer (max 256 bytes) I assembled. It had walk buttons, and some sensors that would simulate the presence of a vehicle. May 2 '13 at 6:07

You kids, get off my lawn!

384 bytes is plenty of space to create something quite complex in assembler.

If you dig back through history to when computers were the size of a room, you'll find some truly amazing feats of artistry executed in <1k.

For instance, read the classic Story of Mel - A Real Programmer. Admittedly, those guys had 4096 words of memory to play with, the decadent infidels.

Also look at some of the old demoscene competitions where the challenge was to fit an "intro" into the bootblock of a floppy, typical targets being 4k or 40k and usually managing to include music and animation.

Edit to add: Turns out you can implement the world's first $100 scientific calculator in 320 words. Edit for the young 'uns: • Floppy = floppy disk. • Bootblock = 1st sector of the floppy read at bootup. • Demoscene = programming competitions amongst hacker groups. • Assembler = fancy way of programming a device if you're too soft to use 8 toggle switches and a "store" button. • The Atari 2600 game console had only 4KB of storage in the ROM game cartridges (though some games got around this limitation by using bank switching to access more than 4K). May 1 '13 at 21:21 • Eons ago I made a pretty realistic bird-chirp (enough that people looked for the bird rather than suspecting the computer), the guts of which (but not the randomizing code that kept it from making exactly the same sound each time) would have rattled around in 384 bytes and I had the additional restrictions of no writable addresses and a zero byte wasn't permitted in the binary. May 2 '13 at 2:19 • I need to get out more, remembered this from back in the day - screen saver in 368 bytes: aminet.net/package/util/blank/368blanker May 2 '13 at 14:14 • +1 for "The Story of Mel". One of the greatest things I've read all week. May 2 '13 at 14:53 • @JohnU: The first few games on the Atari 2600 were all 2K. Many developers never designed any games that went beyond 4K, because even though 8K chips were affordable (and some companies' carts simply used half of a 4K chip) adding bank-switching to a card using a standard (active-low chip-select) chip increased the number of support chips from one to three. May 2 '13 at 16:45 Microcontrollers are sufficiently cheap that they are often used to do really simple things that in years past would more likely have been done with discrete logic. Really simple things. For example, one might want a device to turn on an output for one second every five seconds, more precisely than a 555 timer would be able to do.  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  That would be a real, usable, application, in less than 32 words (48 bytes) of code space. One could easily add a few options to have some I/O pins control timing options and still have lots of room to spare, but even if all the chip did was precisely what's shown above it might still be cheaper and easier than any alternative using discrete logic. BTW, the clrwdt instructions could be moved into the subroutine, but doing so would make things less robust. As written, even if a glitch causes the return-address stack to get corrupted, the watchdog won't get fed until execution returns to the main loop. If that never happens, the watchdog will reset the chip after a couple seconds. • Honestly, you could optimise your code a bit, setting a bad example to the children - 5 separate calls to wait1sec??? Wastrel! ;) May 1 '13 at 16:38 • @JohnU: FYI, the code uses separate calls because if it used a count-to-zero counter and the count got glitched, the loop might run 255 times rather than four, while feeding the watchdog once per second. While it would be possible to guard against that by checking on each loop whether the count was in range, the code to do that ends up being more complicated than five calls and five clrwdt instructions. This isn't the most absolutely-failsafe counter arrangement possible, but some consideration is given to safety issues (e.g. the avoidance of clrwdt within a subroutine). May 1 '13 at 18:52 • @coder543: In the absence of things like power-supply noise, not very. On the other hand, in parts without a brown-out detector, it's possible for all sorts of crazy things to happen if VDD falls to a level between the minimum operating voltage and ground, and then rises back to normal. One should generally try to ensure that any state in which the device may finds itself will revert to normal in a reasonable period of time. The two seconds or so for the watchdog to kick in may be unavoidable, but four minutes for a glitched counter to reach zero might be a bit much. May 1 '13 at 19:38 • @coder543, they happen more often at an important demo than you want to believe. This kind of thinking is also required when building deeply embedded things that have no means to call for help or report an error. Or are inaccessible (think deep sea or outer space) even if an error did get noticed. May 1 '13 at 21:11 • @JohnU: I did notice it, but figured that explaining why I wrote the code as I did might be helpful. Incidentally, I was also trying to show that small tasks can fit in a small processor even if they're not optimized absolutely perfectly. May 2 '13 at 16:39 "ONLY" 384 bytes? Way back in the day, I had the job of writing an entire operating system (by myself) for a specialized computer that served the ship, pipeline, and refinery management industry. The company's first such product was 6800 based and was being upgraded to 6809, and they wanted a new OS to go along with the 6809 so they could eliminate the license costs of the original operating system. They also were bumping up the boot rom's size to 64 bytes, up from 32. If I recall correctly - it WAS about 33 years ago! - I convinced the engineers to give me 128 bytes so I could put the whole operating system's device drivers on the rom and thus make the whole device more reliable and versatile. This included: • Keyboard driver with key debounce • Video driver • Disk drive driver and rudimentary file system (Motorola "abloader format", IIRC), with built-in ability to treat "banked" memory as if it were really-fast disk-space. • Modem Driver (they got the FSK backwards, so these modems only talked with each other) Yes, all of these were as bare-bones as it gets, and hand-optimized to remove every extraneous cycle, but perfectly serviceable and reliable. Yes, I shoehorned all of that into the available bytes - oh, it ALSO set up interrupt handling, the various stacks, and initialized the real-time / multi-tasking operating system, prompted the user on boot options, and booted the system. A friend of mine who's still affiliated with the company (its successor) told me a few years ago that my code is still in service! You can do a LOT with 384 bytes... • you say boot rom, and you mention moving drivers onto the boot rom... this indicates to me that there was a secondary storage medium available. In this discussion, we've already determined that you cannot load code from external storage on this PIC. May 2 '13 at 21:00 • @coder543 That misses the point: 384 bytes is enough to do quite a lot! The original question read like a complaint that 384 wasn't enough to do anything useful - it was more than I needed - a LOT more - to provide all the fundamental components of a real-time, multi-tasking operating system... May 2 '13 at 21:09 • That is the sort of project I'd love to work on, and I often wish I was around in that era of computing so I could truly experience the thrill of making something so powerful with so little. Don't get me wrong, I think it is incredible what technology has enabled today, and it is awesome that people can create complex programs without memory concerns, but I personally enjoy the more limited machines of yesteryear. And it is nice that they were simple enough for a single person to fit the whole workings of their computer in their head. Can't do that anymore... Apr 22 '20 at 0:59 You can use this for very small applications (e.g. delayed PSU start, 555 timer replacement, triac-based control, LED blinking etc...) with smaller footprint than you'd need with logic gates or a 555 timer. • I just noticed that those first two examples were from Stack itself! well played. May 1 '13 at 16:56 • +1 for mentioning footprint. Sometimes size is everything. May 1 '13 at 16:56 One thing that I haven't seen mentioned: The microcontroller you mentioned is only$0.34 each in quantities of 100. So for cheap, mass-produced products, it can make sense to go to the extra coding trouble imposed by such a limited unit. The same might apply to size or power consumption.

• That was exactly my first thought. Also: If I would be a startup with a neat idea, but only few hundred bucks loose, stuff like this can mean the difference between get-back-to-day-job and quit-day-job. Jun 24 '15 at 8:44

I designed a humidity sensor for plants that tracks the amount of water the plant has and blinks an LED if the plant needs water. You can make the sensor learn the type of plant and thus change its settings while running. It detects low voltage on the battery. I ran out of flash and ram but was able to write everything in C code to make this product work flawlessly.

I used the pic10f that you mention.

Here is the code I made for my Plant Water Sensor. I used the pic10f220 since it has an ADC module, it has the same memory as the pic10f200, I'll try to find the Schematic tomorrow.

The code is in spanish, but its very simple and should be easily understood. When the Pic10F wakes from sleep mode, it will reset so you have to check if it was a PowerUp or a reset and act accordingly. The plant setting is kept in ram since it never really powers down.

MAIN.C

/*
Author: woziX (AML)

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

#include "main.h"

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

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

for(;;)
{
//Se checa si es la primera vez que arranca
if(FIRST_RUN())
{
Ciclos = 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--;
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
{
WDT_POST128();
CLRWDT();
TRIS_LEDON();
LEDON();
__delay_ms(1000);
}

{
SLEEP();
}

//Checamos que Humedad_Ref sea mayor o igual a 4 antes de restarle
{
{
WDT_POST32();
SLEEP();
}
}

{
WDT_POST64();
SLEEP();
}

{
{
WDT_POST128();
SLEEP();
}
}

SLEEP();
}
}

{
LEDOFF();
__delay_us(12);
GO_nDONE = 1;
while(GO_nDONE);
}

//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();
__delay_us(150);        //Delay largo para que se baje el voltaje de la bateria
GO_nDONE = 1;
while(GO_nDONE);
LEDOFF();
return (ADRES > LOWBAT ? 1 : 0);
}

{
while(veces)
{
veces--;
WDT_POST64();
TRIS_LEDON();
CLRWDT();
LEDON();
__delay_ms(18);
LEDOFF();
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_LEDON()            TRIS = 0b1011
#define TRIS_LEDOFF()           TRIS = 0b1111

//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 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
/*
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 BateriaBaja (void);

#endif


Let me know if you have questions, I'll try to answer based on what I remember. I coded this several years ago so don't check my coding skills, they have improved :).

Final Note. I used the Hi-Tech C compiler.

• I'd actually be really interesting in reading how you did this. Did you take any notes at all while you were doing it that you wouldn't mind sharing on the webs?
– user23375
May 2 '13 at 13:14
• Hello RhysW, I believe I still have the code. It was really simple actually. I could send you my code if you are interested. Let me know. The circuit I designed is very simple and cool, only 3 resistors, one p-channel mosfet (for reverse battery protection) a 100nF cap and an LED. I use and internal diode in the pic10f to use as a reference for battery measurement and to keep the ADC readings constant. May 2 '13 at 16:36
• That sounds like a neat project. Is there any chance you could post the details here (or at least post them somewhere and link to them)? May 3 '13 at 2:17
• Hello scrafy! Please, if you have something to add to an answer, use the "edit" link instead of posting a new answer, since this site uses voting and doesn't work like a forum. May 3 '13 at 8:11

When I was in high school, I had a teacher that insisted that light dimming was too difficult a task for a student such as I to tackle.

Thus challenged I spent quite a bit of time learning and understanding phase based light dimming using triacs, and programming the 16C84 from microchip to perform this feat. I ended up with this assembly code:

'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
'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


Of course you'd need to modify this for the chip you mention, and maybe add a cheap serial routine for input since your chip doesn't have an 8 bit wide port to listen to, but the idea is that a seemingly complex job can be done in very little code - you can fit ten copies of the above program into the 10F200.

You can find more project information on my Light Dimming page. Incidentally I never did show this to my teacher, but did end up doing a number of lighting rigs for my DJ friend.

Well, years ago I wrote a temperature controller with serial I/O (bit-banging the serial I/O because the MCU didn't have a UART) and a simple command interpreter to talk to the controller. MCU was a Motorola (now Freescale) MC68HC705K1 which had a whopping 504 bytes of program memory (OTPROM) and about 32 bytes of RAM. Not as little as the PIC you reference, but I remember having some ROM left over. I still have a few assembled units left, 17 years later; wanna buy one?

So yeah, it can be done, at least in assembly.

In any case, I've written very simple C programs recently that would probably have fit inside 384 bytes when optimized. Not everything requires large, complex software.

You can write a blink a LED with 384 bytes program memory, and even more.

As far as I know, it is not possible to extend the program memory with an external chip (unless you're building a full ASM interpreter in the 384 bytes, which would be slow). It is possible to extend data memory with an external chip (EEPROM, SRAM) though.

• It wouldn't be hard to build a Turing machine simulator in 384 bytes... May 1 '13 at 15:23
• @ChrisStratton I meant a full interpreter, so that the 'extended program memory' would've the same features as normal.
– user17592
May 1 '13 at 15:24
• Yes, that's what I suggested a means of tightly implementing. The rest is just compiler design... May 1 '13 at 15:25
• If one wanted program logic to be stored in an external EEPROM, trying to emulate the PIC instruction set would not be the way to go. A better approach would be to design an instruction set which was optimized for use with the virtual machine; indeed, that is the approach that Parallax took with their "Basic STAMP" in the 1990's. It was a PIC with 3072 bytes of code space, paired with a serial EEPROM chip. May 1 '13 at 15:39
• BTW, an additional note about the BASIC stamp: it was introduced at a time when flashed-based or EEPROM-based microcontrollers were comparatively rare, but serial EEPROM chips were fairly cheap. For applications that didn't need much speed, a fixed-code micro with a serial EEPROM part would be cheaper than a comparably-sized EEPROM or flash-based micro. The design of the BASIC Stamp wouldn't make sense today, but it was quite practical when it was introduced. May 2 '13 at 16:43

It's actually worse than you think. Your linked Mouser page is confusing matters when it specifies this processor as having 384 bytes of program memory. The PIC10F200 actually has 256 12-bit words of program memory.

So, what can you do with that? The 12-bit PIC instruction set used by the PIC10F20x devices are all single-word instructions, so after you subtract a few instructions for processor setup, you're left with enough space for a program of about 250 steps. That's enough for a lot of applications. I could probably write a washing machine controller in that kind of space, for example.

I just looked over the available PIC C compilers, and it looks like about half of them won't even try to emit code for a PIC10F200. Those that do probably put out so much boilerplate code that you might only be able to write an LED flasher in the space left. You really want to use assembly language with such a processor.

• You are right about the 256 instruction words. Actually one of them is taken up with the oscillator calibration constant, so you get 255 usable instructions. Also, the 10F200 doesn't use the usual PIC 16 14-bit instruction set. It uses the PIC 12 12-bit instruction set. However, I agree with your basic premises. I've done lots of useful things with a PIC 10F200. +1 Jan 4 '15 at 16:35
• @OlinLathrop: I've clarified the answer. I got the PIC16 term from page 51 of the datashseet, but I've decided it's clearer to just refer to "the 12-bit instruction set." The part prefix isn't a reliable guide to the instruction set used. Jan 4 '15 at 17:51

waving my cane in my day, we had to etch our own bits out of sand!

In 1976 (or thereabouts) the Atari 2600 VCS system was one of the most popular "video game platforms" of the time. In it, the microprocessor (MOSTEK 6507) ran at a blazing ~1 MHz and had ****128 bytes of RAM**.

A second example that I recall of a microcontroller with extremely limited RAM (~128 bytes) was a PIC12F used on a DC-DC converter. This micro also had to use assembly language in order to run at all.

• The OP isn't talking about RAM, he's talking about program space. The program space in the Atari 2600 is in the cartridge, not in the RIOT chip. The 2600 supported program ROMs up to 4 kiB without bank switching. (And some commercial cartridges did do bank switching!) As for your PIC12F example, the OP's got you beat: the PIC10F20x series devices have either 16 or 24 bytes of SRAM. Jan 4 '15 at 16:04