board service considerations in your main loop

Confessed Arduino newbie here (messing with NANOs).

So, looking at the Blink example, it seemed reasonable that delay() is probably a bad choice, especially if this is a simple single threaded no operating system situation. But then I looked at the "Blink without Delay" example, and I'm still concerned. If you are essentially building an app based on one main loop, likely with a well designed state machine as you begin to manage more tasks, don't you still have to include some kind of "boardService()" call to insure background system maintenance (responding to the USB port for example) is not being overlooked? Or have the designers insured all the critical low level stuff is being handled in ISRs that I need not worry about?

(deleted)

Or have the designers insured all the critical low level stuff is being handled in ISRs that I need not worry about?

Define critical low level stuff?

An Arduino does not respond to USB messages ( well most don't ) and serial data is an ISR. Note the blink without delay is an example of a round Robbin multitasking not the preemptive type of Linux and other OSs.

The demo Several Things at a Time is an extended example of BWoD. It may help with understanding the technique.

Note that there is nothing happening which is not being explicitly handled by the code in the demo. There is no background system.

...R

We get people using interrupts to watch buttons and the like here mainly to cover their own blocky code.

When your loop() runs at over to several times over 10KHz it's best to save interrupts for special events rather than ways to get around IT-style do-everything-in-one-step coding. Interrupts are not overhead-free.

A delay(1) wastes 16000 cpu cycles. Drop a line with that in a loop() that runs at 67KHz and it will run blocky at 980Hz.

There's the "system maint" routine that when you use Serial does handle incoming and outgoing serial (64 byte buffers).

You can add to or replace the default code. Make it fat and the rest of the sketch will suffer.

Yes, use state machines for non-trivial tasks. No matter what though should any of the tasks run long in any single pass through loop() unless they HAVE TO.

I don't put for-loop or while loop in my fast-loop() code. I manage indexes and run the iterations over time that just by keeping all task steps short/lean gets the job done quick without blocking the rest. It works very well, I debounce button arrays one button per pass for example.

If I wanted to use AVR's to run loads of busy tasks, I'd split the job between multiple chips or get an ARM board. Have you seen the Due or Zero or any of the Teensy 3.x boards? The Teensy 3.6 has a 180MHz ARM M4F chip with enough resources to run a task-control system that's not a semi-futile joke.

GoForSmoke:
We get people using interrupts to watch buttons and the like here mainly to cover their own blocky code.

When your loop() runs at over to several times over 10KHz it's best to save interrupts for special events rather than ways to get around IT-style do-everything-in-one-step coding. Interrupts are not overhead-free.

A delay(1) wastes 16000 cpu cycles. Drop a line with that in a loop() that runs at 67KHz and it will run blocky at 980Hz.

There's the "system maint" routine that when you use Serial does handle incoming and outgoing serial (64 byte buffers).
https://www.arduino.cc/en/Tutorial/SerialEvent

You can add to or replace the default code. Make it fat and the rest of the sketch will suffer.

Yes, use state machines for non-trivial tasks. No matter what though should any of the tasks run long in any single pass through loop() unless they HAVE TO.

I don't put for-loop or while loop in my fast-loop() code. I manage indexes and run the iterations over time that just by keeping all task steps short/lean gets the job done quick without blocking the rest. It works very well, I debounce button arrays one button per pass for example.

If I wanted to use AVR's to run loads of busy tasks, I'd split the job between multiple chips or get an ARM board. Have you seen the Due or Zero or any of the Teensy 3.x boards? The Teensy 3.6 has a 180MHz ARM M4F chip with enough resources to run a task-control system that's not a semi-futile joke.

Thanks. Yeah that was very helpful. And really, I should have known by looking at the example code(s) that since I don't really see "main()", it is likely that the system sets up what it needs for critical background tasks. Llike the response to the USB for example. You can use the poorer BLINK (with the delay() calls) and the board still responds immediately to the USB port. And it looks like some of the better serial UART code uses an ISR. But I've worked with a few other little boards (such as the Pololu WIXEL, the one with the built in 2.4 Ghz radio), and in every usage example there you must call a library based "boardService()" routine. I believe its to make sure things like the USB, watchdog timers, system timers, and some other items were properly updated

I realize this is just a small 8 bit MCU. But over my lifespan, I've had to write device handlers and communication drivers in devices that also were small and also had no OS, and they were expected to offer near "real time" performance. So yeah, I'm big into state machines :slight_smile: , and I guess I've gotten into the habit of asking these kind of questions, rather than assuming it doesn't matter.

People forget how much can be done with a fast little 8 bit controller, and rule number one is always to avoid spinning your wheels in any kind of delay loop.

Thanks for the informative post. I'm new here an it often seems questions are met with snappy and often sarcastic answers. :slight_smile:

The USB capable chips used in some Arduinos, such as the 32U4, seem to do all the USB stuff with dedicated hardware resources that don't impact on your main loop. All of that is handled with purpose-built hardware which just serves up serial data to your sketch.

That's not entirely true and the ARM-based chips certainly have more involvement of the main CPU with USB tasks but the Arduino environment makes it simple for you.

You noticed there's no main() function? That's provided for you as part of the environment.

PeterPan321:
But I've worked with a few other little boards (such as the Pololu WIXEL, the one with the built in 2.4 Ghz radio), and in every usage example there you must call a library based "boardService()" routine. I believe its to make sure things like the USB, watchdog timers, system timers, and some other items were properly updated

AVR's integrates more than some MCU's I guess but Arduino IDE fits more than AVR's. Whatever in boardService() is needed probably gets compiled in (might need a #include) for those boards that don't run their own watchdog, timers, whatever. Arduino millis() runs off a timer interrupt for instance but if your sketch doesn't use it somehow, I wouldn't be surprised if the compiler left that out.

I realize this is just a small 8 bit MCU. But over my lifespan, I've had to write device handlers and communication drivers in devices that also were small and also had no OS, and they were expected to offer near "real time" performance. So yeah, I'm big into state machines :slight_smile: , and I guess I've gotten into the habit of asking these kind of questions, rather than assuming it doesn't matter.

Okay to be clear here the size of the total code isn't the point with loop() Hz. It's the size of the bites the tasks take. It is all about not-blocking, try and keep task steps shorter than 200us (analog read takes 105us) and if possible < 50us.

I have a serial input keyword matcher that read by read seeks to follow text in a linked word list until reaching the end of a word in that list while printing a trace to demo how it works; it just keeps up with 250000 baud input doing all that. At that rate you have 40us between char arrivals. Is that fast enough?
The way I do it, I have a match right after the last matching char is read and a no-match even quicker. Compare to buffer-then-parse IT methods.

The keyword list can have 255 variable length keywords as written, it and the indexes reside in flash ram. If I split a large dictionary up between many AVR's on a clocked-down SPI bus then almost arbitrarily more keywords can be recognized and trigger code on the list host chip or have it send the match number back to the master, the other chips not sending since they didn't match.

People forget how much can be done with a fast little 8 bit controller, and rule number one is always to avoid spinning your wheels in any kind of delay loop.

I "cut teeth" on programmable calculators before getting a 4 or 5MHz 8085 S-100 with 32K and a 5 1/4" FD at work. We made do and I found ways to make people not have to wait. The parse on the fly technique is one of those, don't waste cycles and there's less wait.
Writing on those old machines and even later ones using DOS, my code owned the hardware. Arduino gives me that back.

A 1MHz 6502 is enough to do a lot even as register-sparse as it is. It's a brilliant little machine. ]
AVR has 32 general use accumulator/index/address registers alone. It tromps the 6502 at 1MHz, can rate 20MHz, OC 24MHz.

Thanks for the informative post. I'm new here an it often seems questions are met with snappy and often sarcastic answers. :slight_smile:

You caught me on a good day, had muh caffeine!

If you want to play with bigger resource (same core) AVR's then look into the Mega250 that can take external RAM. Rugged Circuits sells (hopefully still but I have mine!) the QuadRam 512K board for under $30 shipped. You don't get to use all 512K due to the internal 8K taking the low addresses (and dedicated to stack) but you do get 8 banks of 56K heaps.
I don't have any big thing to do with mine, I just liked it, but there's something a person could go nuts with all sorts of ways.

The biggest resource DIP (DIY for can't-SMT) AVR is "The Mighty" ATmega1284P-PU, just got 2 at $5.51 ea.
It's a 40 pin DIP (size of 8088) with 32 IO pins as 4 ports. 16K RAM, 4K EEPROM, 128K flash. 2 serial ports as well as SPI and I2C. Note that all ATmega serial ports are capable of being full speed master-mode SPI ports. Even a 328P can be an SPI slave on its SPI port while mastering its own SPI bus to perhaps serve one or more SD cards as a subsystem.

This is knowledge, technique and software to program AVR chips on breadboard or any Arduino board with ICSP header.

Not sure what you mean by USB. If it's about the serial communication with e.g. the PC

If you call Serial.begin(), it enables the interrupts related to the serial port of e.g. the 328 micro. For both transmit and receive, a software buffer is implemented.

If a receive interrupt happens, the received byte is placed in the receive software buffer; it's your responsibility to read that data before the the buffer is full or you will loose received data.

If you print something (e.g. Serial.print("Hello world")), the data is placed in the transmit software buffer (exception is the first byte if the buffer is empty, that is placed immediately in the UART TX register). Every time the transmission of a byte is complete, the transmit interrupt occurs and a byte from the transmit software buffer is placed in the UART TX register (till there are no bytes to transmit).

If you print more than the software TX buffer can hold, your code will slow down; it will wait till there is space in that buffer.

===

Note that not all functionalities are interrupt driven; a function like analogRead() will start a A-to-D conversion and wait till it's completed.

PeterPan321:
likely with a well designed state machine as you begin to manage more tasks

That's a bit of a leap: rather than "likely" go with "maybe" or even "probably not" :wink:

Thanks for the good info folks. Special thanks to @GoForSmoke. Good to know there are some higher horsepower options should I outgrow my NANO-NANO, and even better news to know they too are available on the cheap if you peek around. I got my NANO clones pretty darn cheap too, which is why my interest was peeked! I still do dozens of projects regularly with analog (Op-amp) logic and discrete logic chips, if only because its often a cheap solution to a one-off idea, and also because I've a huge junk box, and have learned to cheaply fab SMD PCBs from the friendly folks in China. Almost every one of those projects could have been better served with a cheap programmable block. Of course MicroChip has some goodies, but even that can't beat the prices I've "discovered" for some NANO clones. All I have to do now is pick out some equally cheap keypads LCD (or LED) displays, and I'll be able to instantly bring a lot of my old-but-still-usable designs up out of the stone age.

Yes that's exactly the kind of thing I was getting at @sterretje. If most functionality that needs background maintenance done are handled for me in ISRs by the existing setup calls, I'm probably overthinking what someone else has already thunk for me! :slight_smile: But, I will have to study those sources and learn how to set up ISRs for myself. The day may come when I need to make some kind of communications monitor for word lengths beyond what a UART can normally handle, and I wouldn't attempt anything like a bit banger without an ISR. Good to know about not overloading the existing transmit buffer though. That could be improved with a callback function. Not that I need to right now, but eventually I'll have to explore whether libraries can be modified. I quite often liked to add callback functions in my own libraries, that would call an outside function via function pointer, IF it's pointer was not NULL. Then, a newly added "install" function could be used to fill that pointer when needed. So in a case like yo described, I might add something to the internals of the SerialPrint(), so that on detection of a buffer full condition, an outside function could be called to do something useful during the wait. But this is probably moot, because I don't think there's any way to put any executable code that could be modified at run time, (such as an empty function pointer) into RAM. Here I go thinking up a storm before I've finished MY first cup of coffee.

Interrupts is one of the subjects covered on Nick's electronics section. All are really well done.

https://www.gammon.com.au/forum/index.php?bbtopic_id=123

I don't like to use IRQ unless I have to, I'd like to leave the ones already running in Arduino alone.
There are times when it has to be an IRQ but I still see if I can engineer around needs I dun like.

If you can deal with surface mount then those factor AVR's are cheaper and take less room plus the more advanced AVR chips don't come in DIP at all.

Arduino is not limited to AVR's. There are many ARM-duinos out there, the Teensy 3.6 has a 180MHz M4F chip and SD onboard. The Intel Galileo is one with a big fast pentium SoC (no longer being made, but stock may be found), the Yun is another badass, there's others and there's ones I likely never heard of.

What the heck, put an OS on it and I begin to lose interest.

PeterPan321:
But this is probably moot, because I don't think there's any way to put any executable code that could be modified at run time, (such as an empty function pointer) into RAM. Here I go thinking up a storm before I've finished MY first cup of coffee.

There's more than one AVR Forth that does extend itself into flash during runtime. Are you familiar with Forth?

We do have an AVR bootloader available that enables writing to flash in runtime, it's on GitHub.

Using function pointers including arrays of them is certainly possible on Arduinos. Table them up in flash and copy the one you want to a pointer in RAM, you can modify that much. OTOH if you want to go poking machine code in at run, even on a 2560 with extra ram it would probably not be worth doing.

The last self-modifying/writing code I wrote was in Basic back in 1980.
edit: Oh wait, and a good bit of Forth later on, and C++...... but they did it in good ways, not my Basic.

GoForSmoke:
Arduino is not limited to AVR's. There are many ARM-duinos out there, the Teensy 3.6 has a 180MHz M4F chip and SD onboard. The Intel Galileo is one with a big fast pentium SoC (no longer being made, but stock may be found), the Yun is another badass, there's others and there's ones I likely never heard of.

I've had my eye on those "Teensy" boards for some time. :slight_smile: And hell, its fun to do more with less, right? I look back with a smile at some of the communication routing systems I did with early and el-cheapo Maxim chips. But of course they provided a really good IDE with simulation so good, you could design and test before you even burned a chip!

GoForSmoke:
There's more than one AVR Forth that does extend itself into flash during runtime. Are you familiar with Forth?

We do have an AVR bootloader available that enables writing to flash in runtime, it's on GitHub.

Using function pointers including arrays of them is certainly possible on Arduinos. Table them up in flash and copy the one you want to a pointer in RAM, you can modify that much. OTOH if you want to go poking machine code in at run, even on a 2560 with extra ram it would probably not be worth doing.

The last self-modifying/writing code I wrote was in Basic back in 1980.
edit: Oh wait, and a good bit of Forth later on, and C++...... but they did it in good ways, not my Basic.

I remember READING a lot about Fourth years ago when it first dawned on the Horizon, but I never looked at it, nor "learnt' any. I do see flash write calls in the library, so its possible I guess. I tend to use run time flash writes (when available) to let a user of the end product retain all setting changes. I don't know about the ARM chips, but many flash chips in general require full page (whatever a page is) read/writes, and many must actually do a full page erase/write to make a change. For a block of saved settings in a safe place, that makes sense. But in a block of code? What if the code executing the read/modify/erase/write cycle were in the same place in RAM as what you're altering to change that function pointer, or in an area being used by the current function for stack. There may be a method that worked, but I'd be shy about it. Sounds kind of like asking someone to remove their own brain tumor. :slight_smile:

You should save user settings in EEPROM but that's more limited. Yes, flash should be written in full pages says the docs.

328P (Nano) has 1K EEPROM. You can add serial EEPROM/RAM/flash on SPI or I2C bus.

Normal AVR bootloaders do not allow writing to flash after the hex file is uploaded.
AVR bootloader is to allow programming via serial port.