/* main.cpp - Main loop for Arduino sketches Part of megaTinyCore - github.com/SpenceKonde/megaTinyCore Copyright (c) 2018~2021 Spence Konde, (c) 2005-2013 Arduino Free Software - LGPL 2.1, please see LICENCE.md for details */ #include /* Required by some libraries to compile successfully. Even though it's nonsense in Arduino. */ int atexit(void ( * /*func*/)()) { return 0; } void initVariant() __attribute__((weak)); /* Weak empty variant initialization function. The purpose is unclear. It sounds like it was intended * initialize the variant, and specific variants would have their own implementation. But in practice * it seems to be instead used as an initialization callback that libraries can use to run code before * setup, like FreeRTOS - it would have been nice if the official API included such a callback. */ void initVariant() { } void __attribute__((weak)) onPreMain(); void __attribute__((weak)) onPreMain() { /* Override with any user code that needs to run WAY early, in .init3 */ } void __attribute__((weak)) onBeforeInit(); void __attribute__((weak)) onBeforeInit() { /* Override with any user code that needs to run before init() */ } uint8_t __attribute__((weak)) onAfterInit(); uint8_t __attribute__((weak)) onAfterInit() { /* Override with any user code that needs to run before interrupts are * enabled but after all other core initialization. * return 1 to not enable interrupts) */ return 0; } int main() __attribute__((weak)); /* The main function - call initialization functions (in wiring.c) then setup, and finally loop * * repeatedly. If SerialEvent is enabled (which should be unusual, as it is no longer a menu * * option even, that gets checked for after each call to loop). Note that _pre_main() is * * called first in non-optiboot configurations (neither is needed on Optibooot configurations() * * an extra bit of initialization code in .init3 to fix the vectors and still happen if user * * overrides main. In the past there was a USB-related function here, that is removed, as work * * will be needed in any event at the core level if VUSB-based "stuff" arrives, but really I'm * * just waiting for the DU-series now */ int main() { onBeforeInit(); // Emnpty callback called before init but after the .init stuff. First normal code executed init(); // Interrupts are turned on just prior to init() returning. initVariant(); if (!onAfterInit()) sei(); // enable interrupts. setup(); for (;;) { loop(); } } #if (!defined(USING_OPTIBOOT)) /*********************************** CHECK RESET FLAGS ******************************************/ /* If we are not using Optiboot, we need to check the reset flagss, and reset via software for * * a clean start. Unfortunately, if we clear the registers here, we'll prevent user code from * * seeing them, which isn't helpful. As documented in the reset guide, we suggest overriding * * this function with your own version. One example is included below and others in that guide * * init_reset_Flags() should be overridden with one of the ones from the reset guide in any * * production code. * * If using optiboot, this will never be called, because Optiboot does the same thing. * * By the time app runs, the flags will have been cleared and moved to GPIOR0* (it needs to * * clear flags to honor bootloader entry conditions, so I didn't have a choice about that. * * This function is called before *anything* else, so the chip is a blank slate - or it's * * state is unknown. You're probably running at 4 MHz unless it was a dirty reset, in which * * case it could be anything. No timekeeping is possible, period. The only exception is the * * WDT reset timer with is independent of the HF oscillators and is designed to reset you out * * of hangs amd bad states fthat you end up with when a bug causes the code but not the * * hardware to reset, * * Interrupts are disabled, or in event of dirty reset * * LVL0EX bit will block them. In the event of a clean reset, nothing is set up. There is no * * PWM, no timekeeping of any millis/micros/delay see no time passing and and all delays, * * delay_microseconds, _delay_ms() and _delay_us(), are the wrong length because they are * * based on F_CPU. * * If you end up here from a dirty reset, you know nothing about the configuration of the * * peripherals. Check the flags, save them if you need them, and maybe turn on an LED while * * waiting for the WDT to trigger. If you're debugging something really nasty, you can try to * * gather data about the nature of the fault. For example, turn on an LED if\ LVL0EX is set * * meaning you got here from a missing ISR. With one of those little boards with 6 LEDs on * * (many are available reasonably cheaply on aliexpress et al.) end up being very useful * * for this sort of thing. * * * The register in question is GPIOR0 on megaTinyCore, GPR.GPR0 on Dx-series, but both names * * are aliases of each other per core_devices for compatibility */ /* Minimum: Reset if we wound up here through malfunction - this relies on user clearing the * * register on startup, which is rarely done in Arduino land. */ void __attribute__((weak)) init_reset_flags() ; void __attribute__((weak)) init_reset_flags() { uint8_t flags = RSTCTRL.RSTFR; RSTCTRL.RSTFR = flags; if (flags == 0) { _PROTECTED_WRITE(RSTCTRL.SWRR, 1); } GPIOR0 = flags; } #endif /* If using SPM from app, but not actually using Optiboot, we need to point the vector tables in the right place. * since the "application" is actually split across "boot" and "application" pages of flash... and it's vectors * are all in the section defined as "boot" section, tell the interrupt controller that, otherwise nothing'll work! * This could just as well be set in init() but for the fact that we support overriding main(). I don't know if * anyone who is doing that wants to use my flashwrite library, but it seems plausible. * And while we way you need to take full responsibility for setting up the part if you do, nobody is going * to figure this out; that's not a reasonable expectation. * We also at the same time make sure there's a reset flag. We can't clear it, even though that * needs to be done becauwe then it wouldn't be there if user needed it. But we will document the * need to clear it and suggest overriding init_reset_flags(), and give the examples. */ /* So we need to do 1 or 2 things - as long as we're not using Optiboot, we should force a * * software reset if we don't see any reset flags on startup - init_reset_flags() does that, * * Then if we're using SPM from app, we need to also flip the it that move s the interrupts * * to the start of flash. */ /************************************************************************************************** * INITIALIZATION FUNCTIONS LOCATED ANYWHERE SPECIAL GO HERE! * * * * They *MUST* be declared with both the ((naked)) ahd ((used)) attributes! Without the latter, * * the optimizer will eliminate them. Without the former, the sketch will not start... * * Wait what? Yeah, it was generating a and outputting a ret instruction, which caused the * * sketch to return to nowhere under certain conditions and never reach main() at all. * * I do not understand how the old vector fixer allowed the sketch to start ever... but * * since it was only compiled in when flash write was enabled it could have been missed for a * * long time. * **************************************************************************************************/ #if (!defined(USING_OPTIBOOT)) void _initThreeStuff() __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".init3"))); // this runs, as the name implies, before the main() function is called. #if !defined(SPM_FROM_APP) // If we're not doing the SPM stuff, we need only check the flags void _initThreeStuff() { init_reset_flags(); onPreMain(); } #else /* Otherwise, SPM_FROM_APP is defined */ #if defined(__AVR_Dx__) /* So this better be a Dx!!! */ /******************************************* * THIS FUNCTIONALITY IS ONLY EXPOSED ON * * DX-SERIES PARTS SO THIS CODE CANT-HAPPEN * * megaTinyCore. You must write to flash * * using optiboot if required * *******************************************/ // if we are, we also need to move the vectors. See longwinded deascription above. void _initThreeStuff() { init_reset_flags(); _PROTECTED_WRITE(CPUINT_CTRLA,CPUINT_IVSEL_bm); onPreMain(); } #if (SPM_FROM_APP == -1) /* Unlimited SPM from app and no bootloader, means we need to stick entry point on page 0 */ /* Declared as being located in .trampolines so it gets put way at the start of the binary. This guarantees that * it will be in the first page of flash. Must be marked ((used)) or LinkTime Optimization (LTO) will see * that nothing actually calls it and optimize it away. The trick of course is that it can be called if * the user wants to - but it's designed to be called via hideous methods like * __asm__ __volatile__ ("call EntryPointSPM" : "+z" (zaddress)) * see Flash.h */ /* It must be located *before everything* - including PROGMEM, which the compiler puts ahead of .init. * .trampolines however comes before progmem. The function must be naked, it must be used, and you need to guard it * with the rjmp that hops over the spm and ret instructions unless you jump directly to the entrypoint. */ void __spm_entrypoint (void) __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".trampolines"))); void __spm_entrypoint (void) { __asm__ __volatile__( "rjmp .+4" "\n\t" // Jump over this if we got here the wrong way "EntryPointSPM:" "\n\t" // this is the label we call "spm z+" "\n\t" // write r0, r1 to location pointed to by r30,r31 with posrincewmwr "ret"::); // by 2, and then return. } #endif /* SPM from app unlimited */ #else #error "The selected part has SPM_FROM_APP = unlimited, isn't using Optiboot, or it's not a Dx-series. You must change one of those things" #endif /* Only AVR Dx supports this */ /* End if DXCore only spm from app stuff */ #endif // Finally, none of these three things need to be done if running optiboot! // We want the vectors in the alt location, it checks, clears, and stashes the reset flags (in GPR0) // and it providews the entrypoint we call to write to flash. #else void _initThreeStuff() __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".init3"))); void _initThreeStuff() { onPreMain(); } #endif