Arduboy Kickstarter version design discussion

Technically no, but it feels a lot nicer. If you found a Leonardo hardware item that ran at 8Mhz and used that as your choice it should compile correctly - just as it does now for 16Mhz Leonardo - and you’d just need the software to step it down during the boot cycle. Of course if someone is going to burn a new bootloader or set fuses it make sense to have those things codified in our own boards.txt file, that’s what the file is for - for custom hardware built on Arduino.

If desired, at some point the sketch could call setSystemSpeed(SPEED_8MHZ) to return to 8MHz, as if switching to 16MHz had never happened.

This is of course doable (much anything is in software) but has all sorts of complications - for delay routines, for hand-crafted assembly, for timers, for USB/SPI clocks, etc. Almost all of the Arduino core stuff wants to know the speed at compile time and a bunch of low-level changes happen as a result of that. There is already a library that does this somewhat (prescaler.h), but it provides it’s own delay, millis, micros functions that you use that are altered to be aware of the currently running clock.

Switching speeds multiple times while running isn’t something I’d think most programs would ever need or want to do.

If we had PWM our tone function could use it. It’s not something game developers would even need to think about. They’d just get more CPU for free.

This is why I recommend using Arduino pin 5 as the primary, or only, speaker pin. This is processor pin PC6. It can be complimentary PWM output A for Timer 4, which is the high speed timer. It can also be PWM output A for Timer 3, which is a 16 bit timer. One, probably minor, draw back of using this pin is that it’s currently used on the Dev Kit for the Right button, so Right would have to be reassigned.

I chose Arduino pin 13 for the second speaker pin (if we decide to have dual output capability) for a similar reason. This is processor pin PC7. It can be the direct PWM output A for (high speed) Timer 4. The only issue I have with using pin 13 is that it’s not available on a Pro Micro but this is only a minor consideration.

With both of these pins each connected to a separate lead of the speaker, for each pin you could independently choose one of:

For pin 5 (PC6):

  • A solid low (or high, it makes no difference)
  • A software generated signal
  • PWM from 16 bit Timer 3
  • (complimentary) PWM from high speed Timer 4

For pin 13 (PC7):

  • A solid low (or high, it makes no difference)
  • A software generated signal
  • (direct) PWM from high speed Timer 4

Actually, there’s one more possibly for each pin, which is “set as an input”. This would effectively act as a mute for the other pin.

For audio, it makes no difference if the PWM is direct or complimentary. However, though I can’t say for sure, I think if you set both pins to output PWM from timer 4 the volume of sound would be higher than if one pin were set to a solid low. This dual volume level capability might prove to be another benefit of having two outputs.

Also, if dual volume, as a result of generating a single ended or complimentary PWM signal, actually works, then it would also work for software generated single ended or complimentary signals.

There’s a bit of tradeoff here. Although you get more flexibility being able to use both Timer3 and Timer4 (a solid benefit for those developers sticking with tone() …) you can get much of the same benefit in high freq PWM mode with two different comparator outputs (A, B, D) from Timer4 alone. That doubling up could only come from selecting two lines from any two of the Timer4 pair sets 13/5, 10/9, and 6/12.

However, it appears that most if not all of 10/9 and 6/12 are already used.

Ultimately, though, even one PWM pin will be a big lead over straight bit-bashing (as it is in the dev kit).

So how about we keep pin 5 for the primary output and switch to pin 12 for the secondary?

We would still be able to use both Timer3 and Timer4, because pin 5 can be Timer3A and pin 12 can be Timer4D. For different comparators on Timer4, pin 5 can be (complimentary) Timer4A and pin 12 can still be Timer4D.

Pin 12 is currently assigned to the display reset signal but this just needs to be a standard digital output, so could be moved to any other pin. We could just swap them and put display reset on pin 13.

Note that by “primary” output I just mean the one we highly recommend to be used if you’re just using a single output, with the “secondary” output set to a solid low or used for mute. This is really only to provide better compatibility with a Pro Micro, which doesn’t have pin 12 (or pin 13). Another reason to designate pin 12 as secondary is because it’s not considered to be PWM capable, from an Arduino IDE point of view.

I wasn’t tracking the display setup closely; I had just been recalling that the display used the SPI bus and display pins could not be moved because of dedicated functionality.

A 5/12 pair would likely be the ultimate in flexibility. And just to put icing on it, I think it would be possible to use carefully chosen complementary comparator setups in 4A and 4D so that the effective output is strictly complementary signals, even though the two outputs are ‘complementary’ outputs themselves.

This now all hangs on someone dropping that piezo sounder between a pair of digital outputs, and testing the mixing functionality. And - measuring the current during full pin voltage differential to ensure that the protective resistor function is exactly that.

From what I can tell, anyone wanting all pins on a compatible board could go with the Leonardo, right?

To some extent. The pins are there, so you could test all the mixing functionality and various output modes using a Leonardo or Micro (not Pro Micro).

For current measurement, the Leonardo and Micro run at 5V, whereas the Arduboy will run at either battery voltage or (hopefully) 3.3V. However, any 3.3V system could be used for testing since just generating complimentary tones is required, so other pins could be used. Plus, if it turns out to be safe at 5V then it will certainly be safe at lower voltages.

I have a 3.3V Pro Mini that I could use with a suitable sketch. I don’t have the exact same piezo speaker that the Arduboy uses, though.

In any case don’t think excessive current will flow if any piezo speaker is used. The impedance of a piezo speaker is quite high even at its resonant frequency, where impedance is lowest. If it does end up to be a problem, a (very inexpensive) resistor could be placed in series.

As you’ve mentioned, another thing that should be determined is if some sort of low pass filter is required for anti-aliasing. If aliasing turns out to be a problem, but can be solved with a simple first order RC filter, it wouldn’t cost much to add.

It’s probably good to get these things sorted out as soon as possible, before the final hardware design needs to be locked down.

I’ve found that putting a speaker across two pins, and generating tones by setting the pins to opposite levels, does result in a noticeable increase in volume over using a single pin.

I took the source code for the standard Arduino Tone library and modified the functions so that you specify a second pin and a volume flag. If the volume flag is false the second pin is just held low, so the sound generated is the same a with the original tone() function. With the volume flag set to true, the pins will be set to opposite levels, for higher volume.

The library is called VolTone and I’ve put it on GitHub. Included there is an example sketch, VolToneScale that plays a musical scale of tones, each at low then high volume.

I recorded the output from an 18mm piezo disk as file VolToneScale.mp3. The Arduboy’s speaker will be smaller but I suspect it will sound similar to this.

I haven’t yet tried to generate different tones on each of the pins, to hear how well they mix.

Not to nitpick, but couldn’t you also have done this my building a class that consumed Tone (vs modifying) and just made the appropriate lower level Tone calls itself? Just a thought. Might be easier to maintain over time unless this is purely an experiment :smile:

Yes, it was only intended to be a quick hack for an experiment. The fact that I modified even the sections that are #ifdefed out is just because I didn’t feel like figuring out which would actually end up being compiled. Plus, I think I ended up modifying most of the low level calls and ISRs as well, so there wouldn’t be much original code that wasn’t overridden.

I don’t really expect anyone to actually use this (although I guess they could) and probably won’t maintain it, but I’ll leave it there in case anyone wants to use it as a reference.

I’ve done some design and testing of this and it appears to work well.

Design:
The specific resistor divider ratio isn’t critical, as long as the output is below 3.3V at the highest battery voltage, since we can do the math in software. However, I though it would be good to have each ADC step be somewhat meaningful, instead of some arbitrary fraction of a volt. By making full scale be 5.115V (5115mV), which is 5 times the ADC maximum count of 1023, each ADC step will be 5mV. This means we want the resistors to divide the voltage by 5115/3300 = 1.55.

The total resistance of the divider also isn’t critical. It should be a good deal lower than the impedance of the ADC input, but high enough as to not draw too much current. The ATmega32U4 datasheet states:

A divider with a 10K impedance would draw a fair amount of current. Since we’ll only be checking the battery voltage occasionally, we can afford longer sampling times. I feel that a total divider resistance of around 500K would be suitable. For accurate readings, the resistor tolerance should be 1% or better. Using a 187K resistor for the high side and 340K for the low side gives us exactly a divide by 1.55 with a total resistance of 527K. These are both standard EIA E96 1% values, so should be readily available.

Testing:
I used a linear variable power supply in place of a battery. I connected the divider circuit to pin A3 of my Arduboy compatible system and loaded the following sketch:

// Battery monitor test

/*
A resistor divider is used to scale the input voltage to be within a
0V to 3.3V measurable range. A 187K resistor goes between the
battery positive terminal and an analog input. A 340K resistor goes
between the same analog input and ground.

With this divider, a regulated 3.3V on AVCC, used as the ADC
reference, will give 5mV per step.
Full scale will be 1023 * 5mV = 5.115V.
*/

#include <SPI.h>
#include <EEPROM.h>
#include <Arduboy.h>

const int batPin = 3;
Arduboy aboy;

void setup() {
 aboy.start(); 
 aboy.setTextSize(2);
}

void loop() {
  uint8_t bgLow, bgHigh;

  // Display battery voltage in millivolts
  aboy.clearDisplay();
  aboy.setCursor(0, 8);
  aboy.print("BAT: ");
  aboy.print(analogRead(batPin) * 5);
  delay(100);

  // Display raw bandgap reading
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring
  bgLow  = ADCL; // must read ADCL first - it then locks ADCH 
  bgHigh = ADCH; // unlocks both
  aboy.setCursor(0, 32);
  aboy.print("Bgap: ");
  aboy.print((unsigned int)((bgHigh<<8) | bgLow));
  
  aboy.display();
  delay(900);
}

I changed the supply voltage in 0.1V steps and noted the displayed readings. The following is a graph of the results:

All of the readings were stable and repeatable, changing by no more than 1 LSB (with a filter capacitor added. See Additional Design below).

All of the battery readings are about 1% low because the measured output of the voltage regulator, and thus the reference, was 3.33V, which is about 1% higher than the nominal 3.3V.

At a voltage slightly below 2.6V, the program stopped running, presumably due to the CPUs brown out detection circuitry.

The graph shows that the bandgap readings start to rise below around 3.4V input. This is because the input voltage to the regulator is below the dropout voltage, so the reference has gone lower while the bandgap remains the same. In this area, the battery readings stay constant because the reference is now changing directly with the battery voltage.

So, as I expected, seeing a rise in the bandgap reading can be used alone to detect a low battery condition. However, being able to accurately read the battery voltage when it’s above 3.4V would give us the ability to display it, and/or have a battery charge remaining indication.

Here is a table of the actual readings that I took and graphed:

Supply mV  Reading mV  Bandgap raw
 2600        3350          432
 2700        3350          416
 2800        3350          402
 2900        3350          388
 3000        3350          375
 3100        3350          362
 3200        3350          351
 3300        3350          341
 3400        3365          332
 3500        3465          332
 3600        3565          332
 3700        3665          332
 3800        3765          332
 3900        3865          332
 4000        3965          332
 4100        4065          332
 4200        4160          332
 4300        4260          332
 4400        4360          332
 4500        4460          332
 4600        4560          332
 4700        4660          332
 4800        4760          332
 4900        4860          332
 5000        4955          332

Additional design:
Because of the relatively high resistance of the divider, and the fact that the battery voltage will change very slowly, it may be a good idea to put a capacitor between the ADC input pin and ground, as a noise filter. In my testing I found that successive readings of the same input voltage could change by +/- one or two steps. Adding a 0.1uF ceramic capacitor eliminated these changes. I would add a footprint on the circuit board for such a capacitor, which could just be left unpopulated if it were decided it wasn’t needed.

As indicated in the ATmega32U4 datasheet, a 0.1uF capacitor should be placed between the AREF pin and GND. Nothing else shoud be connected to this pin, since we’re using AVCC internally as the reference.

The more precise and stable the ADC reference is (along with the tolerances of the divider resistors), the more accurate the battery readings will be. As with the resistors, the 3.3V regulator output should be within 1% or better. I’d recommend using the Micrel MIC5205-3.3YM5 as the regulator. It has all the desirable qualities, and is inexpensive and widely available.

From a system software standpoint, it would be nice to have a reserved EEPROM location to store the “battery not low” bandgap value constant, since it will vary from unit to unit. Being able to compare the present bandgap reading to a permanent stored value would make it easier to detect a low battery, than looking for a rise in the bandgap over time.

1 Like

Nice work on this. Would be great to see in the final production Arduboy.

I’ve now tried this and can say that it works nicely.

I installed the Arduino Tone Library with an updated Tone.cpp which has been modified to work with the ATmega32U4.

I wrote the following test sketch and ran it with a piezo speaker connected directly across pins A2 and A3:

#include <Tone.h>

Tone tone1, tone2;

void setup() {
  tone1.begin(A2);
  tone2.begin(A3);
}

void loop() {
  tone1.play(NOTE_C4);
  tone2.play(NOTE_E4);
  delay(2000);
  tone1.stop();
  tone1.play(NOTE_G4);
  delay(2000);
  tone2.stop();
  tone2.play(NOTE_C5);
  delay(2000);
 
  tone1.stop();
  tone2.stop();
  delay(1000);
}

Each of the two tones mixed well and sounded good. I tried a few other tone pairs and they also mixed fine.

So, I’d have to say that putting the speaker across two pins to provide mixing, as well as to provide a simple dual volume level capability, is a pretty good idea. Again, the only drawback is, if only a single pin is being used to generate audio, the other pin must be set as an output (or set as an input to mute the speaker).

So, partly in summary - if the external hardware is configured this way, improper configuration or software usage may not produce desired outcomes. But there is no configuration or software usage that can cause hardware damage.

That seems like a fairly reasonable setup. Use it right, and you get sounds. Use it wrong, and you get silence. If you go back to using it right, you get sound, not an audio-bricked Arduboy.

Since this also provides for the option of processor-intense fast PWM sound reproduction, but also low-rate straight audio PWM, developers have a good range of options.

Yes, that is correct.

A bonus eureka thought! (verified) :smile:

I tried setting one pin to INPUT while outputting a tone on the other pin, which muted the speaker as expected.

I then tried changing the pin from input to INPUT_PULLUP, which would place an internal 20K to 50K resistor in series between the speaker and Vcc. This resulted in the tone sounding at a lower volume.

So, by placing the speaker across two pins we can use one of them to control the volume of the other, by setting the control pin as follows:

  • Mute: Set as INPUT
  • Low volume: Set as INPUT_PULLUP
  • Normal volume: Set as OUTPUT
  • High volume: Set to the opposite level (compliment) of the other pin.

There may be other useful possibilities using INPUT_PULLUP (which I haven’t yet tested):

  • A normal volume tone mixed with a low volume tone. The tone on one pin is generated by toggling the output high and low. The tone on the other pin is generated by toggling between a low output and input_pullup.

  • Two low volume tones mixed, by generating each tone by toggling between output low and input_pullup.

  • A single ??? volume tone by toggling one pin between output low and input_pullup and setting the other pin to the opposite.

  • A single ??? volume tone by toggling one pin between low and high output. The second pin is set to output low when the other is high, but set to input_pullup when the other is low

  • Other combinations and techniques?

Wow this thread keeps on rolling, I’m reading through this don’t have time to give detailed comments yet but I know many people have asked about the 8mhz vs 16mhz.

The plan for now is to do my best to support 16mhz by adding a voltage boost controller to bring us up to 5v so that 16mhz will be in spec

Also, I’m planning to put all the input buttons on the same port so they can all be read with one port read command.

And finally, I’ll be moving the speaker to a PWM pin, I think pin 6 was recommended?

Keep on rolling with this I’ll be trying to lock down the design by the end of July early August. Thanks guys!

1 Like

I hope this doesn’t affect your profit margins too much, and you can make one from thin enough components.

Just keep in mind that you will still have to power the display’s VDD logic supply at 1.65V to 3.3V. So, you’ll probably need a regulator for the display, in addition to the 5V booster for the CPU.

And, as I’ve already mentioned previously, the display inputs are only tolerant of voltages up to the VDD supply voltage. Running the ATmega32U4 at 5V means its outputs used to control the display will be 5V when high. This means you will need level shifters on the display inputs: MOSI, SCLK, CS, DC and RST.

If you use a 5V booster then the technique to detect low battery, by seeing a rise in the bandgap vs. VCC, will likely not work. If battery monitoring is still desired, connecting the battery directly to an analog input pin would work if the booster output was exactly 5V with a tight tolerance, and remained so with the battery between full charge voltage and 3.4V.

Alternatively, if the display ends up being powered by a stable, accurate 3.3V regulator (such as a Micrel MIC5205), then both previously discussed battery monitoring methods could be used if the 3.3V output was connected to the CPUs AREF pin.

Only Port D could do all 7 buttons and Port F could do 6, as I summarised in this post. In the following post, @Dreamer3 argues that there may not be much value in doing this, anyway. I tend to agree. I would concentrate more on the value of the secondary functions of the pins.

Most of the following is just reiterating what I’ve said in the original post, with some input and consensus from others. Please refer to it for more details.

It would be desirable to leave pins 0/RXD1(PD2), 1/TXD1(PD3), 2/SDA(PD1), 3/SCL(PD0) and TXLED (PD5) unused and connected to pads for hacking and expanding the Arduboy or creating enhanced devices that are Arduboy software and library compatible. This rules out using Port D, leaving 6 pins on Port F.

Avoiding pins 11, 12, 13, A4, A5 if possible would help maintain compatibility with a SparkFun Pro Micro.

It’s desirable to have the start button on a pin that can generate an external interrupt to wake the unit from a low power sleep mode. It’s also desirable to have that interrupt be a standard Arduino one, so the attachInterrupt() standard library function could be used. Ruling out the above mentioned pins 0 - 3, that leaves pin 7 (int.4).

@Dreamer3 has said that he would like to see buttons A and B be interrupt capable as well. No pins on Port F can generate interrupts.

Abandoning the idea of getting all the buttons on one port, here’s how I would map the buttons:

  • Start: 7 (PE6) Arduino int.4
  • Candidates for A and B: 8 (PB4), 9 (PB5), 10 (PB6)
  • Candidates for the D-pad, after A and B are mapped:
    A0 (PF7), A1 (PF6), A2 (PF5), A3 (PF4) none can generate an interrupt.
    8 (PB4), 9 (PB5), 10 (PB6)

Unless we wanted one D-pad button to be interrupt capable, using A0 - A3 would put all the D-pad buttons on Port F, so they could all be read at once. Buttons A and B could both be read at once from Port B.

No, pin 5 (PC6) is recommended. If we wanted the dual-pin mixing and volume capabilities discussed in previous messages in this thread, the recommended pin for the other lead of the speaker is pin 12 (requiring the display reset signal to be moved to a different pin such as 13 (PC7) or 11 (PB7) ).

1 Like

+1 for added H/W functionality at the cost of doing 2x port reads for button status; a good trade-off.

It should be pretty obvious to most people (who stop to think about it). Unless you need every single BYTE of space, having the buttons on even say 7 ports doesn’t really matter. You only need to poll them during your event loop - and only the simplest games can really use only buttons = SINGLE_PORT type code. A lot of games need to track long/short buttons pressed and either the previous button state or a whole continuation of button states (so you can do things like fire every half second a button is held)… and as soon as you get to that point there is a lot of button management code being run in addition to just button polling. Grouping buttons smartly onto a few ports as mentioned above seems like a great plan.

Even if you argue “every byte matters”, there are MUCH easier places to trim fat from in the current Arduboy core lib than the button code. Places that require changing no hardware at all.

To me A & B make more sense to be hardware interruptible than D-PAD… I can imaging picking it up and hitting those to wake it (without knowing anything about this future start buttons or where it might live).

1 Like