Skip to content

Instantly share code, notes, and snippets.

@joeycastillo
Created September 19, 2022 03:24
Show Gist options
  • Save joeycastillo/97678bb1c151f4943203cfffb21a71eb to your computer and use it in GitHub Desktop.
Save joeycastillo/97678bb1c151f4943203cfffb21a71eb to your computer and use it in GitHub Desktop.
Low power test for Feather M0
// MIT license, Joey Castillo 2022
// works with Alex Taradov's bare metal SAM D21 project:
// https://github.com/ataradov/mcu-starter-projects
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "samd21.h"
#include "hal_gpio.h"
//-----------------------------------------------------------------------------
#define BLINK_PERIOD_SECONDS 15
HAL_GPIO_PIN(LED, A, 17)
//-----------------------------------------------------------------------------
static void sys_init(void) {
// Switch to 8MHz clock (disable prescaler)
SYSCTRL->OSC8M.bit.PRESC = 0;
// make sure main oscillator stays off in standby
SYSCTRL->OSC8M.bit.RUNSTDBY = 0;
// set up external 32k crystal oscillator
SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP(0x7) | SYSCTRL_XOSC32K_EN32K |
SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_RUNSTDBY;
SYSCTRL->XOSC32K.bit.ENABLE = 1;
while(!SYSCTRL->PCLKSR.bit.XOSC32KRDY);
// connect external crystal to GCLK1
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1);
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_XOSC32K |
GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN;
while(GCLK->STATUS.bit.SYNCBUSY);
}
//-----------------------------------------------------------------------------
static void timer_init(void) {
PM->APBCMASK.reg |= PM_APBCMASK_TC3;
// clock TC3 with GCLK1 (the 32k crystal)
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TC3_GCLK_ID) | GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN(1);
// Run in standby, divide 32768 Hz clock by 1024 (32 ticks per second)
TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ |
TC_CTRLA_PRESCSYNC_RESYNC | TC_CTRLA_RUNSTDBY |
TC_CTRLA_PRESCALER_DIV1024;
// set the counter to overflow at our desired interval
TC3->COUNT16.COUNT.reg = 0;
TC3->COUNT16.CC[0].reg = (32768 / 1024) * BLINK_PERIOD_SECONDS;
// at first boot, cheat the count so that the LED blinks on in a half second
TC3->COUNT16.COUNT.reg = TC3->COUNT16.CC[0].reg - (32768 / 2048);
// enable the counter
TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
// and enable the interrupt
TC3->COUNT16.INTENSET.reg = TC_INTENSET_MC(1);
NVIC_EnableIRQ(TC3_IRQn);
}
//-----------------------------------------------------------------------------
void irq_handler_tc3(void) {
// if the interrupt flag is set
if (TC3->COUNT16.INTFLAG.reg & TC_INTFLAG_MC(1)) {
// clear the interrupt flag
TC3->COUNT16.INTFLAG.reg = TC_INTFLAG_MC(1);
}
}
//-----------------------------------------------------------------------------
int main(void) {
// initialize the clocks
sys_init();
// initialize our counter
timer_init();
// set up LED output and turn the LED off at first
HAL_GPIO_LED_out();
HAL_GPIO_LED_clr();
// set SLEEPDEEP bit so that __WFI enters standby
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
while (1) {
// enter standby mode and stay there until the timer interrupt fires
__WFI();
// at this point we just woke up! Toggle the LED.
HAL_GPIO_LED_toggle();
// and if we just turned on the LED, set the count so that the interrupt
// fires again in a half-second, turning it back off.
if (HAL_GPIO_LED_read()) {
TC3->COUNT16.COUNT.reg = (32768 / 1024) * BLINK_PERIOD_SECONDS - (32768 / 2048);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment