Go Down

Topic: AT SAM D21 running at 1.8V issue (Read 1 time) previous topic - next topic

columba02

     I have designed a custom Arduino compatible data-logger at University as part of my dissertation. I am using a SAMD21G18A powered at 1.8V.The design is based on the Arduino MkrZero schematic. The problem is that the microcontroller stops working at 2.1V using the Arduino MkrZero/Zero bootloader. If I upload an example code from Atmel Studio the microcontroller stops working around 1.65V, as in the datasheet.

    Does anybody know what part of the Arduino environment is responsible for setting this voltage limit ?



ron_sutherland

Brown-out detection (BOD)?

The datasheet talks about BOD12 and BOD33, I just skimmed it.

Perhaps look over the bootloaders to see what shows up.

https://github.com/arduino/ArduinoCore-samd/search?q=BOD33&unscoped_q=BOD33
my projects: https://github.com/epccs

columba02

Is the bootloader for the Arduino Mzero compatible with the one for the MkrZero/Zero ? I couldn't find any settings in the Zero bootloader.

lcarvao

@columba02 Did you find out the limitation for running the SAMD21 at 1.8v in the arduino IDE?

I've disabled the BOD33 using a programmer, but hitting the same problem when using Arduino IDE. When I try lowering the voltage to 2.1, the mcu slightly tries to bring it back to 2.2v, but when I insist a little longer on the 2.1 or lower it locks on the last state.

The only way to bring it back up and running is by supplying 2.2v again and pressing the reset button.

dhalbert

I have been working with lcarvao on this, and believe it's due to the number of NVM wait states required when running at 48 MHz and 1.8V.

if you want to run at 48Mhz and 1.8V, you must increase the number of NVM wait states (NVMCTRL->CTRLB.RWS). You only need 1 wait state at 48MHz and 3.3V, but you need 3 wait states at 48MHz and 1.8V.


The Arduino bootloader and the Arduino core assume you only need 1 wait state, and so does the Microsoft/Adafruit UF2 bootloader. For background, see https://github.com/adafruit/uf2-...



From the datasheet:


lcarvao

That's correct.

Increasing NVM wait states works.

So, to operate SAMD21 at lower voltages we need to:
1 - disable BOD33 fuse
2 - increase NVM wait states to 3.


thank you Dhalbert

JKPina

Hi all! nice thread, still trying to run my MKR1000 @1.8V, its processor is SAMD21 as well, so I assume
this thread is valid.

short question:
 In which files are you modifying either BOD33 fuse and NVM wait states? (and what is the bootloader's name you are using)


Long question:
related to previous post:

That's correct.

Increasing NVM wait states works.

So, to operate SAMD21 at lower voltages we need to:
1 - disable BOD33 fuse
2 - increase NVM wait states to 3.


thank you Dhalbert
I dived into the repository https://github.com/arduino/ArduinoCore-samd/tree/master/bootloaders/zero

(for MKR1000 is said that use the same bootloader than Zero). There is board_init.c, where is possible to modify NVM wait states, but can't find where to disable BOD. Are you using a variant of the bootloader other than "mzero" and "sofia"?


Best regards!

MartinL

#7
Jun 22, 2020, 11:44 am Last Edit: Jun 22, 2020, 11:46 am by MartinL
Hi JKPina,

The number of NVM wait states is set in the bootloader's "init_samd21.c" file, detailed in the discussion on github here: https://github.com/adafruit/uf2-samdx1/issues/119.

The eaiest way to set the fuses is using a programmer such as Atmel ICE or similar and Atmel Studio. In Atmel Studio select Tools->Device Programming then uncheck the BOD33 checkbox and click on the program button:



Alternatively, it's possible to set the fuses programmatically, as in this example from the Atmel Community forum:   https://community.atmel.com/forum/samd21-fuse-settings-causing-programming-failure.

MartinL

#8
Jun 22, 2020, 01:09 pm Last Edit: Jun 22, 2020, 01:29 pm by MartinL
Hi JKPina,

If you simply run the short sketch below just one time, it will disable the BOD33 fuse, (all other fuses remain unaffected):

Code: [Select]
// Disable the BOD33 fuse in the SAMD21's user row word 0
void setup() {
  SerialUSB.begin(115200);                                    // Start serial communication on the native USB port
  while(!SerialUSB);                                          // Wait for the console to open
  SerialUSB.println(F("Fuse settings before:"));
  SerialUSB.println((*(uint32_t*)NVMCTRL_USER), HEX);         // Display the current user word 0 fuse settings
  SerialUSB.println((*(uint32_t*)(NVMCTRL_USER + 4)), HEX);   // Display the current user word 1 fuse settings
  uint32_t userWord0 = *((uint32_t*)NVMCTRL_USER);            // Read fuses for user word 0
  uint32_t userWord1 = *((uint32_t*)(NVMCTRL_USER + 4));      // Read fuses for user word 1
  NVMCTRL->CTRLB.bit.CACHEDIS = 1;                            // Disable the cache
  NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;               // Set the address
  NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_EAR |                // Erase the auxiliary user page row
                       NVMCTRL_CTRLA_CMDEX_KEY;
  while(!NVMCTRL->INTFLAG.bit.READY)                          // Wait for the NVM command to complete
  NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;                 // Clear the error flags
  NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;               // Set the address
  NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_PBC |                // Clear the page buffer
                       NVMCTRL_CTRLA_CMDEX_KEY;
  while(!NVMCTRL->INTFLAG.bit.READY)                          // Wait for the NVM command to complete
  NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;                 // Clear the error flags
  *((uint32_t*)NVMCTRL_USER) = userWord0 & ~FUSES_BOD33_EN_Msk;  // Disable the BOD33 enable fuse in user word 0
  *((uint32_t*)(NVMCTRL_USER + 4)) = userWord1;               // Copy back user word 1 unchanged
  NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_WAP  |               // Write to the user page
                       NVMCTRL_CTRLA_CMDEX_KEY;
  while(!NVMCTRL->INTFLAG.bit.READY)                          // Wait for the NVM command to complete
  NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;                 // Clear the error flags
  NVMCTRL->CTRLB.bit.CACHEDIS = 0;                            // Enable the cache
  SerialUSB.println(F("Fuse settings after:"));
  SerialUSB.println((*(uint32_t*)NVMCTRL_USER), HEX);         // Display the current user word 0 fuse settings
  SerialUSB.println((*(uint32_t*)(NVMCTRL_USER + 4)), HEX);   // Display the current user word 1 fuse settings
}

void loop() {}

Note that the code shown in the link to the Atmel Community forum has a bug, as the user word 1 is at address NVM_USER + 4 and not NVM_USER + 1.  The increment by 4 is necessary to skip over user word 0, (32-bits = 4 bytes).

JKPina

Thanks a lot @MartinL for your support! I will try and then post here.
cheers!

Go Up