Hi everyone,
I'm working on a project that utilizes analog sensors that are essentially wired as voltage dividers, and I need low sleep power, thus I can't (simply) power the sensors from the 3v3 output of the regulator. The sensors each draw about 2.4mA, so assuming the specs given for the SAMD21 (by basically everyone that sells it) are correct, that each GPIO pin can supply 7mA, it should be no problem to power each sensor this way, especially if using pins on different clusters. All that should be needed is a digitalWrite high for the supply pin, analogRead the sensor output, digitalWrite low for the supply pin, then sleep.
However, when actually applying that load to the output pin and measuring the voltage, instead of 3.3V (my DMM usually reads 3.301V for no-load pin voltage), I was seeing about 2.97V. Not terrible, but a 10% error is definitely noticeable in the sensor readings. After a little searching, turns out the M0 has both a low-power and high-power mode for output pins, and low-power mode is 2mA, not 7mA - so my 2.4mA load might be a significant problem! To get 7mA, it seems two things need to happen, according to the datasheet in section 36.8.1:
- VDD must be 3V-3.63V; no problem when Moteino M0 is powered by a lithium battery
- PORT.PINCFG.DRVSTR = 1; definitely a problem in the Arduino IDE by default
I made a few changes to the Moteino samd core files to make this possible in the Arduino IDE, without having to jump through too many hoops. This is basically done in two ways, with some redundancy I think:
- It seems like the most robust way is to set DRVSTR in the digitalWrite function (in wiring_digital.c and wiring_digital.h) to ensure it's configured for each digitalWrite.
- The other way is to set DRVSTR in the pinMode function, but from what I've read, digitalWrite may override this setting, and indeed, if I use the standard digitalWrite function, setting DRVSTR in the pinMode function does not seem to last.
For the digitalWrite approach, I simply made an additional digitalWrite function (I called it digitalWriteHP) that is a copy of the normal function with a single line added directly after the pinMask definition in wiring_digital.c:
uint32_t pinMask = (1ul << pin); //existing code
PORT->Group[g_APinDescription[ulPin].ulPort].PINCFG[g_APinDescription[ulPin].ulPin].bit.DVRSTR = 1; //additional line
This also required the new function to be added to the wiring_digital.h file.
For the pinMode approach, I added a new mode in the pinMode function, called "OUTPUT_HIGH," and added a corresponding case in the switch. I copied the existing OUTPUT case and added the same line of code. This required a definition for OUTPUT_HIGH to be added to the wiring_constants.h file; I simply added:
#define OUTPUT_HIGH (0x4)
This alone, however, will not work, as the standard digitalWrite function seems to reset this value.
So now in the Arduino IDE I can do things like
or
I think an even more seamless way to do this would be to add an argument to the digitalWrite function to represent the desired power state, so the normal calls can be made, and include a default argument to handle existing code, where the default is low-power. Perhaps another way would be to add an additional mode for the standard digitalWrite function, something like "HIGH_HP," and adding a corresponding case to the switch after the "LOW" case that copies and adds to the default case. I'll give this a try and see if it works.
Anyway, the results of setting the output pin to high power mode were, according to my CurrentRanger's OLED screen and my DMM:
- in low-power mode: 2400-2402mA with 2.975V supplied
- in high-power mode: 2423-2426mA with 3.211V supplied
It would be awesome if the Moteino M0 core files could be updated to add this functionality, since I'm sure other people will want to have more than 2mA available from an output pin, but if that's too much to ask, I hope this post helps at least for users to modify the files themselves. I'm going to try a few approaches and try to propose a best/simplest one, and post an update.