I have an ATSAMR21B18-MZ210PA, which is an ATSAMR21E18 packaged with MX25L2006E flash. The SAMR21 is itself a SAMD21 paired with an AT86RF233 wireless module. So that's a SAMD21 + low power wireless + flash.
The datasheets say I should expect ~4uA current draw from the SAMR21 in standby mode and the AT86RF233 in deep sleep (page 1061 of the second datasheet linked above) and an additional ~5uA from the MX25 flash in deep power down mode (no more than 45uA in regular standby—page 28 of the third datasheet linked above).
However, I cannot get current draw to drop below ~0.55mA. Below I have copied my main
entry point. What might I be missing or doing wrong?
#define RTC_TICKS_PER_SECOND 1
#define SECONDS_PER_TICK 1
#define BLINKY_PIN PIN_PA07
#define SLEEP_MODE_DEEP 3
#define AT86RF233_SLP_TR PIN_PA20
#define AT86RF233_RST PIN_PB15
#define MX25_CS PIN_PA27
int main(void)
{
/* Initializes MCU, drivers and middleware */
atmel_start_init();
// Initialize the blinky pin
initBlinkyPin();
// Set AT86RF233 into low power state
gpio_set_pin_direction(AT86RF233_RST, GPIO_DIRECTION_OUT);
gpio_set_pin_level(AT86RF233_RST, false); // Active low reset
delay_ms(1); // Hold it there for a little bit
gpio_set_pin_level(AT86RF233_RST, true); // Release reset
// Drive SLP_TR high to signal it should enter sleep
gpio_set_pin_direction(AT86RF233_SLP_TR, GPIO_DIRECTION_OUT);
gpio_set_pin_level(AT86RF233_SLP_TR, true);
// Set MX25 flash chip into deep sleep
struct io_descriptor *io;
spi_m_sync_get_io_descriptor(&SPI_0, &io);
spi_m_sync_enable(&SPI_0);
gpio_set_pin_direction(MX25_CS, GPIO_DIRECTION_OUT);
gpio_set_pin_level(MX25_CS, true);
delay_ms(1);
// Select MX25 flash chip
gpio_set_pin_level(MX25_CS, false);
// Read status register until WIP bit is false
uint8_t readStatusRegisterCommand[] = { 0x05 };
uint8_t statusRegister[1];
while (1)
{
io_write(io, readStatusRegisterCommand, 1);
statusRegister[0] = 0xff;
io_read(io, statusRegister, 1);
if ((*statusRegister) & 1)
continue;
break;
}
// Send DP command
uint8_t deepPowerDownCommand[] = { 0xb9 };
io_write(io, deepPowerDownCommand, 1);
gpio_set_pin_level(MX25_CS, true); // Release CS at byte boundary
/* Replace with your application code */
while (1) {
sleep(SLEEP_MODE_DEEP); // Stop until interrupt, power down all clocks except those set to run in standby
toggleBlinkyPin();
}
}
I'm not sure what other code I should include—please let me know. I'm quite a novice in the embedded world. I'm using Atmel Studio with Atmel Start to auto generate driver code and wire up clocks for me—I'm still figuring out the files it gives me.
The CPU is clocked at 8MHz from OSC8M. SPI is also driven by that clock. I also have an RTC timer always running from OSCULP32K. The timer is set to generate an interrupt every second.
You can see that the while
loop is putting the MCU into standby mode, toggling a pin when it wakes up. That allows me to see how frequently the MCU is waking up. I can confirm that the pin is toggling once every second.
Before the while
loop you can see my attempts to put the AT86RF233 and MX25 into their respective low power states. I say "attempts" because I don't see an easy way to confirm the correctness of that code. However, my output pin toggles which means execution at least makes it through that code. If I comment it out then current draw increases only slightly (maybe 0.1mA) so it's probably doing something.
I omitted the initBlinkyPin
and toggleBlinkyPin
functions for brevity. There's nothing surprising in them… they just set the state of and drive the output of a single pin.
Edit: Unplugging the programmer (duh!) reduces current to ~0.44mA. Better, but still a couple of orders of magnitude higher than I expect.
I should also say now that the programmer is disconnected from the module there are only the power and ground pins connected to anything when I measure current draw. And I think my multimeter is accurate and precise enough.
Edit 2: I programmed a second identical module and measured current in the same way: 0.52mA. So not only orders of magnitude too high, but also very high variance. These are not Chinese knockoffs. What gives?
Edit 3: I'm cross-posting this on community.atmel.com.
Best Answer
Looks like I wasn't correctly disabling the AT86RF and MX25 modules. Disabling those correctly reduced current draw to ~11uA. More details here: https://community.atmel.com/comment/2630026#comment-2630026
Edit: For future readers, I used the libraries in Atmel's ASF3. There I found function calls implemented by Atmel which disable these peripherals correctly. Here's what my
main
function looked like after that: