FreeRTOS ATMega328-P Port

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi. I'm working on a software framework for the ATMega328-P (with Arduino bootloader), and I'd really like to use FreeRTOS. It's supposed to work on AVRs, but the only ports I've seen for AVR is for the ATMega323 and AVR32. I'm far from proficient enough in the 328, or in AVRs in general, to accomplish porting to the 328. I was hoping someone here might have knowledge of someone who has done this or something similar in the past. I don't mind if it's 'compatible' or not with the Arduino bootloader. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

to accomplish porting to the 328.

You'd be very surprised how simple it is to port it between AVR because the only hardware element of the AVR it uses is a timer for the scheduling ticks. So it's just a case of configuring it to use the timer in the 328 rather than the 323. The rest is "high level" software that would be the same on any AVR

Everything to be changed is in source/portable/GCC/ATMgea323/port.c

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's basically what I thought. However, when I tried changing some simple things in the 'porting' files, then tried to compile, I got multiple 'undefined' errors, usually having to do with registers. As I said, I'm not nearly proficient enough to be able to delve through the source, replacing registers and service calls.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You've piqued my interest (I use mainly 168's) so I'll have a crack at porting it myself and see what happens.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
You've piqued my interest (I use mainly 168's) so I'll have a crack at porting it myself and see what happens.

That would be great, because 168 and 328 are so similar. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK some good news, some bad news:

good news: I copied all relevant files to a single directory so I could easily make it an AVR Studio project. I modified them (I *think*!) so that it should work - it certainly now compiles for 168 or 328 without errors/warnings

bad news: it doesn't (work that is!)

But at least it's a start. One key thing I changed in the config file was to reduce the heap size from 1500 to 500 - 500 fits in both 168 and 328, 1500 only in 328

Build started 17.5.2009 at 15:35:55
avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT comtest.o -MF dep/comtest.o.d  -c  ../comtest.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT crflash.o -MF dep/crflash.o.d  -c  ../crflash.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT croutine.o -MF dep/croutine.o.d  -c  ../croutine.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT heap_1.o -MF dep/heap_1.o.d  -c  ../heap_1.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT integer.o -MF dep/integer.o.d  -c  ../integer.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT list.o -MF dep/list.o.d  -c  ../list.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT main.o -MF dep/main.o.d  -c  ../main.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT ParTest.o -MF dep/ParTest.o.d  -c  ../ParTest.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT PollQ.o -MF dep/PollQ.o.d  -c  ../PollQ.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT port.o -MF dep/port.o.d  -c  ../port.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT queue.o -MF dep/queue.o.d  -c  ../queue.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT regtest.o -MF dep/regtest.o.d  -c  ../regtest.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT serial.o -MF dep/serial.o.d  -c  ../serial.c

avr-gcc  -mmcu=atmega168p -Wall -gdwarf-2 -std=gnu99 -DGCC_MEGA_AVR  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT tasks.o -MF dep/tasks.o.d  -c  ../tasks.c

avr-gcc -mmcu=atmega168p -Wl,-Map=FreeRTOS.map comtest.o crflash.o croutine.o heap_1.o integer.o list.o main.o ParTest.o PollQ.o port.o queue.o regtest.o serial.o tasks.o    -lm  -o FreeRTOS.elf

avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  FreeRTOS.elf FreeRTOS.hex

avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex FreeRTOS.elf FreeRTOS.eep || exit 0

avr-objdump -h -S FreeRTOS.elf > FreeRTOS.lss

AVR Memory Usage
----------------
Device: atmega168p

Program:    9904 bytes (60.4% Full)
(.text + .data + .bootloader)

Data:        748 bytes (73.0% Full)
(.data + .bss + .noinit)


Build succeeded with 0 Warnings...

(EDIT: it's the create of the idle task returning 0xFF rather than 0x01 that is causing it not to run

EDIT2: Oh and that's because it cannot malloc() a TCB - obviously the reduction to 500 was TOO severe!)

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, I was obviously hoping for too much trying to run it in a 168. While it's true that the demo has a number of tasks so maybe there is room to use it in the 1K SRAM of a 168 the fact is I had to stretch the heap size to 650 and stop about 3 tasks from being created in order for the remaining ones to work. It then appeared to work fine (well it kept hitting a breakpoint in the integer maths task every few thousand cycles)

If I put it back to the "323" situation of all demo tasks enabled and the heap size set to 1500 and built for the 328 then it simply just worked. So I'd have said this was a 2K+ (SRAM) operating system for any kind of sensible use.

Actually that probably marries in with my usual thought that there's a complexity barrier around the 32K (code flash) mark where an RTOS is justified. So maybe a pre-emptive OS simply isn't justified or workable in a 16K/1K processor?

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Actually that probably marries in with my usual thought that there's a complexity barrier around the 32K (code flash) mark where an RTOS is justified.

I'm even more of a Luddite than that. Mega88-class, +/- a factor of 2, not only don't need an RTOS but should have very little "structure"--the way to get the most packed into it (and those app classes are likely to be at least somewhat cost sensitive) both size and spped is a near-monolithic main(), lots of globals, few locals, register-variable scratchpads.

Get to your Mega32 class, and then more structure is warranted as the [at least this] programmer can't keep it all in-head. With a programming team the size may get a bit smaller.

Around that class of apps, maybe one notch bigger, I'll allow you to use an RTOS--as long as it is not pre-emptive. I shudder at the waste of SRAM and the cream of the clock cycles for a >>micro<<controller app.

YMMV.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lee,

The reason I got interested in FreeRTOS again was that I'm thinking of doing something with AVR+Ethernet to make a webserver so I can control an LCD and some other stuff across a network. The Wiznet devices comes with TCP/IP and other protocol software but to run something "alongside" I figured one could either analyze the stack and "break in" at the lowest level to schedule in your own stuff or, possibly easier, is to just run the stack stuff in the thread of an RTOS so it remains isolated then do what I want in another task. As the $29 WIZ200WEB has a mega128 and a 32K SRAM chip I'm guessing SRAM limits will not be a problem.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, I'm dabbling as well--but not pre-emptive. ;)

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow. Do you have a list of changes you made, or the modified files?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Cliff

Would FemtoOS be a better choice for the small AVR's

http://www.femtoos.org/

/Bingo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, here's a cut-down set of files. I threw out all the demo files and things like the co-routine support so that what's left is simply a demo of two pre-emptively scheduled tasks in main.c that cycle LEDs on PORTs C and D:

portSHORT main( void )
{
	/* Create the tasks defined within this file. */
	xTaskCreate( mytask1, ( signed portCHAR * ) "LED_C", configMINIMAL_STACK_SIZE, NULL, mainMY_TASK_PRIORITY, NULL );
	xTaskCreate( mytask2, ( signed portCHAR * ) "LED_D", configMINIMAL_STACK_SIZE, NULL, mainMY_TASK_PRIORITY, NULL );

	/* In this port, to use preemptive scheduler define configUSE_PREEMPTION 
	as 1 in portmacro.h.  To use the cooperative scheduler define 
	configUSE_PREEMPTION as 0. */
	vTaskStartScheduler();

	return 0;
}
/*-----------------------------------------------------------*/

static void mytask1( void *pvParameters ) {
static unsigned char mask = 1;
	/* The parameters are not used. */
	( void ) pvParameters;
	DDRC = 0;
	for( ;; )
	{
		PORTC = mask;
		mask <<= 1;
		if (mask == 0) {
			mask = 1;
		}
		vTaskDelay( mainCHECK_PERIOD );
	}
}

static void mytask2( void *pvParameters ) {
static unsigned char mask = 0x80;
	/* The parameters are not used. */
	( void ) pvParameters;
	DDRD = 0;
	for( ;; )
	{
		PORTD = mask;
		mask >>= 1;
		if (mask == 0) {
			mask = 0x80;
		}
		vTaskDelay( mainCHECK_PERIOD );
	}
}

As for the changes I made between these files and the one in the 323 implementation that comes with FreeRTOS they were (according to KDiff3:

FreeRTOS.h:
 no changes

FreeRTOSConfig.h
 configUSE_IDLE_HOOK changed from 1 to 0
 configTOTAL_HEAP_SIZE changed from 1500 to 650
 configUSE_CO_ROUTINES changed from 1 to 0

heap_1.c
 no changes

list.c
 no changes

main.c
 major changes - suggest you compare yourself

port.c
 portCLEAR_COUNTER_ON_MATCH changed from "0x08" to "(1<<WGM12)"
 portPRESCALE_64 changed from "0x03" to "((1<<CS11)|(1<<CS10))"
 portCOMPARE_MATCH_A_INTERRUPT_ENABLE changed from "0x10" to "(1<<OCIE1A)"
 references to "TIMSK" changed to "TIMSK1"
 references to "SIG_OUTPUT_COMPARE1A" changed to "TIMER1_COMPA_vect"

portable.h
 #include "../portable/GCC/ATmega323/portmacro.h" changed to #include "portmacro.h" to flatten directories

portmacro.h
 no changes

projdefs.h
 no changes

queue.c
 removed #include "croutine.h"

queue.h
 no changes

StackMacros.h
 no changes

task.h
 no changes

tasks.c
 no changes

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Actually that probably marries in with my usual thought that there's a complexity barrier around the 32K (code flash) mark where an RTOS is justified. So maybe a pre-emptive OS simply isn't justified or workable in a 16K/1K processor?
The pre-emptive scheduler I wrote (in C++) fits comfortably in a m168 with space for floating point libs and 3 or 4 genuinely useful tasks (in fact it fits in a m88, but you'd struggle to do much of use for lack of RAM). I did look at freeRTOS before writing my own scheduler and decided it was bigger and more complex than I needed. Whether it is "justified" or not I guess depends on your viewpoint.

Christopher Hicks
==

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well to be fair to FreeRTOS. Now that I've ditched all the demo "baggage" that minimal168.rar with the two LED tasks builds with:

AVR Memory Usage
----------------
Device: atmega168

Program:    5206 bytes (31.8% Full)
(.text + .data + .bootloader)

Data:        765 bytes (74.7% Full)
(.data + .bss + .noinit)

So a bit over 5K of code leaving 11K for "other stuff" and the main resource being used there is the 765 bytes out of 1024 bytes of RAM and that's because I've got the heap size set to 650.

Now that I think about it, this is not like normal C where you want to keep the static SRAM at around the 75%..80% mark because there's the worry about the automatics/stack. In an RTOS the OS is handing out stacks to the tasks from the heap so maybe I should wind that 650 figure up to use virtually all the SRAM?

Anyway with it set to 650 I can create 4 tasks, though the OS won't start when I try for 5. Of course that is with each task just using "configMINIMAL_STACK_SIZE" - if I needed to assign more stack to one or more then it would reduce things.

After a bit of experimentation is seems I can only wind the heap size up to about 800 (build gives 89.4% SRAM) before the OS won't start and then I can run a maximum of 5 simple tasks.

Obviously in a 328 or any AVR with 2K+ of SRAM there'd be plenty of room for movement.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got FreeRTOS to run on an Atmel 128. I made the heap size 2000, and have 3 tasks. So far, it works pretty well. It can have some crazy frustrating errors though. I think there is some kind of memory leak.

I've tried to fix this by using static variables in my local functions. Presumably, whenever the task is called, the static variables should just be loaded from stack. I've found that there is a LOT of trial and error involved here.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The (free) AVRx RTOS is very mature and very low overhead.
I've used it.
It is AVR specific, so application code isn't literally portable to non-AVRs.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you look at DuinOS: FreeRTOS ported to the Mega168, Mega328, Mega644, Mega1280 and Mega 1284 (and soon to the Mega32U4 and 16U4):

http://es.wikipedia.org/wiki/DuinOS

Regards,
Julián
http://robotgroup.com.ar

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Someone PMd me to ask how I determined how to port the mega323 FreeRTOS code to mega328p. My reply was..

 

As always it's a question of "porting". When moving code from one CPU to another it's a question of identifying how they are different. Luckily FreeRTOS is not very hardware dependent. It's only hardware use in the original 323 demo is timer1 for the RTOS tick and UART for the serial (the latter could be ignored by removing the demo codd that uses serial) so it's all about the timer really. So that's when you dig out copies of both mega323 and mega328p datasheets. You need to study the code as it is and see which bits it is setting in timer1 setup for 323 then read the 328 data and see does it have the same mode of operation available and is that configured in the same or different ways.

 

Now one gripe I have with their original code is they just used hard coded bit numbers like 0x08, 0x03, 0x10 so I had to start by working out which bits those were in the 323 to determine how they were setting the timer but in reality, though some of the bits moved within registers the two chips were very close so the porting was fairly easy.

 

Bottom line, any porting, really just involves two datasheets and a bit of understanding of the peripherals.

Last Edited: Sat. Jul 13, 2019 - 12:40 PM