Port for generalised Microchip ATmega architecture
This port provides a basis for supporting all modern ATmega devices using either the Enhanced Watchdog Timer, or Timer0 (an 8-bit Timer generally available across the whole range).
This initial commit contains the information required to build with System Tick being generated by either the:
Further commits can add support for 16-bit Timers available on many relevant devices. The availability of these 16-bit Timers is somewhat device specific, and these complex and highly configurable Timers are often used to generate phase correct PWM timing (for example) and they would be wasted as a simple System Tick.
The port also provides support for the 3 byte program counter devices ATmega2560 and ATmega2561. Specific to these two devices the EIND
register need to be preserved during a context switch. Also, due to a limitation in GCC, the scheduler needs to reside in the lower 128kB of flash for both of these devices. This is achieved by adding the .lowtext
section attribute to the function prototype.
To build generic Microchip (AVR) ATmega support the similarities across the family must be considered, and differences respected. Some comments on the strategy follow.
The Microchip (AVR) ATmega family has limited Timer and Pin capabilities, and is designed to be used in physical applications, controlling hardware with PWM and recognising level and edge voltage changes. It does this mainly through the use of 16-bit Timers (for generating phase correct PWM by up/down counting), and Pins attached to Interrupts. The 8-bit Timers are also attached to Pins, and they can be used for more simple timing tasks, requiring only a single counting direction.
The Timers not attached to Pins (and therefore not impacting the application of the device) are some 16-bit Timers (very device dependent, eg Timer3 on 1284p), The RTC Timer, and the Watch Dog Timer.
The Watch Dog Timer is configured identically across most of the ATmega devices. It comes in two variants. 1. Old style (eg ATmega32) which does not have an Interrupt capability, and hence on these old devices cannot be used as the System Tick. and 2. New style enhanced WDT, which can generate an Interrupt, and is available on every relevant device.
Using the Watch Dog Timer (WDT) to generate the System Tick does not impact its use as a watch dog. It can be configured to generate a System Tick interrupt, and then one period later to Reset the device if the interrupt is not serviced.
Configuration and usage of the WDT is covered in <avr/wdt.h>
which was revised in avr-libc 2.0.0.
Two additional WDT functions are provided in port.c
, which extend avr-libc functions to enable the WDT Interrupt without enabling Reset wdt_interrupt_enable()
, and to enable both the Interrupt and the Reset wdt_interrupt_reset_enable()
.
The ATtiny, ATmega, ATxmega families can optionally support both 3 byte PC and 3 byte RAM addresses. However, focusing on just the ATmega family only two devices have a large Flash requiring them to use 3 byte PC. These are the ATmega2560 and ATmega2561. This PR provides support for these two devices in two ways.
portSAVE_CONTEXT()
and portRESTORE_CONTEXT
saving both the RAMPZ and EIND registers.portTASK_FUNCTION_PROTO()
with the linker attribute .lowtext
which is used to ensure that the scheduler and relevant functions remain in the lower 128kB of Flash.For devices which can support XRAM and have the RAMPZ register, this register is also preserved during the context switch.
The ATmega family does not support interrupt nesting, having only one interrupt priority. This means that when the Scheduler is running, interrupts are normally disabled.
When a very time critical process is running, based on microsecond timing generated by one of the Timers, it is important to re-enable interrupts as early as possible in processing a Yield. Fortunately, this is supported through the use of the NO_BLOCK
decorator when defining the Interrupt Service Routine.
The NO_BLOCK
decorator will enable the global interrupt early in the handling of an ISR (in this case for the Scheduler), and enable interrupts to be nested. Using this method, I've been able to successfully implement an Audio Synthesiser with less than 83 microseconds for each cycle, whilst still running the Scheduler to handle display and input.
Using NO_BLOCK
is optional, and should only be done if a critical Timer should interrupt the Scheduler.
Most users of FreeRTOS will choose to manage their own heap using one of the pre-allocated heap management algorithms, but for those that choose to use heap_3.c
, the wrappered malloc()
method, there is an issue that needs to be addressed.
The avr-libc library assumes that the stack will always be above the heap, and does a check for this when responding to a malloc()
request. This is not the case when Tasks are running, as their stack is located in the early allocated heap address ranges which will be below free heap memory, and so the malloc()
request will fail even though heap space is available.
To avoid this issue causing pvPort_Malloc()
to failing, the user needs to issue this tuning statement BEFORE they use the heap, or use the xTaskCreate()
API.
if( __malloc_heap_end == 0 ) __malloc_heap_end = (char *)(RAMEND - __malloc_margin);
Unfortunately in the repository there is nowhere sensible to include this statement as it should be included early in the main()
function.
For devices which can support XRAM the user will need to tune the location of stack and heap according to their own requirements.
ATmega devices with ENHANCED WDT Interrupt capability - will use WDT.
ATmega devices without enhanced WDT Interrupt capability - will use a 8-bit or 16-bit Timer.