1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-01-29 14:52:12 +01:00

PiOS/Win32: Most stable version of port.c so far. Changed Makefile.win32 to use mingw32-gcc to avoid conflicts.

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1278 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
cwabbott 2010-08-12 14:32:52 +00:00 committed by cwabbott
parent e3fb3f3e1b
commit c1cf1ec71b
5 changed files with 81 additions and 258 deletions

View File

@ -41,7 +41,7 @@ USE_BOOTLOADER ?= NO
CODE_SOURCERY ?= NO
# Toolchain prefix (i.e arm-elf- -> arm-elf-gcc.exe)
TCHAIN_PREFIX ?= ""
TCHAIN_PREFIX ?= mingw32-
# Remove command is different for Code Sourcery on Windows
REMOVE_CMD ?= rm
@ -221,7 +221,7 @@ EXTRA_LIBDIRS =
# EXTRA_LIBS = xyz abc efsl
# for newlib-lpc (file: libnewlibc-lpc.a):
# EXTRA_LIBS = newlib-lpc
EXTRA_LIBS = WS2_32
EXTRA_LIBS = WS2_32 Winmm
# Path to Linker-Scripts
LINKERSCRIPTPATH = $(PIOSSTM32F10X)
@ -281,7 +281,7 @@ CSTANDARD = -std=gnu99
# Flags for C and C++ (arm-elf-gcc/arm-elf-g++)
ifeq ($(DEBUG),YES)
CFLAGS = -g$(DEBUGF) -DDEBUG
CFLAGS = -g
endif
CFLAGS += -DARCH_WIN32
@ -331,11 +331,11 @@ LDFLAGS += -lgcc
# Define programs and commands.
CC = $(TCHAIN_PREFIX)gcc
CPP = $(TCHAIN_PREFIX)g++
AR = $(TCHAIN_PREFIX)ar
OBJCOPY = $(TCHAIN_PREFIX)objcopy
OBJDUMP = $(TCHAIN_PREFIX)objdump
SIZE = $(TCHAIN_PREFIX)size
NM = $(TCHAIN_PREFIX)nm
AR = ar
OBJCOPY = objcopy
OBJDUMP = objdump
SIZE = size
NM = nm
REMOVE = $(REMOVE_CMD) -f
###SHELL = sh
###COPY = cp
@ -383,11 +383,13 @@ lss: $(OUTDIR)/$(TARGET).lss
sym: $(OUTDIR)/$(TARGET).sym
hex: $(OUTDIR)/$(TARGET).hex
bin: $(OUTDIR)/$(TARGET).bin
exe: $(OUTDIR)/$(TARGET).exe
# Default target.
#all: begin gccversion sizebefore build sizeafter finished end
#all: begin gccversion build sizeafter finished end
all: elf
#all: elf
all: exe
ifeq ($(LOADFORMAT),ihex)
build: elf hex lss sym
@ -489,9 +491,15 @@ endif
# use $(CC) for C-only projects or $(CPP) for C++-projects:
$(CC) $(THUMB) $(CFLAGS) $(ALLOBJ) --output $@ $(LDFLAGS)
# $(CPP) $(THUMB) $(CFLAGS) $(ALLOBJ) --output $@ $(LDFLAGS)
#create a windows-usable .exe file
$(OBJCOPY) $(OUTDIR)/$(TARGET).elf $(OUTDIR)/$(TARGET).exe
# Link: create EXE output file from object files.
.SECONDARY : $(TARGET).exe
.PRECIOUS : $(ALLOBJ)
%.exe: $(ALLOBJ)
@echo $(MSG_LINKING) $@
# use $(CC) for C-only projects or $(CPP) for C++-projects:
$(CC) $(THUMB) $(CFLAGS) $(ALLOBJ) --output $@ $(LDFLAGS)
# $(CPP) $(THUMB) $(CFLAGS) $(ALLOBJ) --output $@ $(LDFLAGS)
# Assemble: create object files from assembler source files.
define ASSEMBLE_TEMPLATE
@ -576,6 +584,7 @@ clean_list :
$(REMOVE) $(OUTDIR)/$(TARGET).bin
$(REMOVE) $(OUTDIR)/$(TARGET).sym
$(REMOVE) $(OUTDIR)/$(TARGET).lss
$(REMOVE) $(OUTDIR)/$(TARGET).exe
$(REMOVE) $(ALLOBJ)
$(REMOVE) $(LSTFILES)
$(REMOVE) $(DEPFILES)
@ -604,5 +613,5 @@ endif
# Listing of phony targets.
.PHONY : all begin finish end sizebefore sizeafter gccversion \
build elf hex bin lss sym clean clean_list program
build exe elf hex bin lss sym clean clean_list program

View File

@ -24,7 +24,7 @@
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ ( ( portTickType ) 1000 )
#define configTICK_RATE_HZ ( ( portTickType ) 200 )
#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 45 * 1024 ) )

View File

@ -90,6 +90,7 @@ typedef struct tskTaskControlBlock
tskTCB *debug_task_handle;
/*
Win32 simulator doesn't really use a stack. Instead It just
keep some task specific info in the pseudostack
@ -102,37 +103,49 @@ typedef struct
HANDLE hThread; /* handle of thread associated with task */
HANDLE hSemaphore; /* Semaphore thread (task) waits on at start and after yielding */
portSTACK_TYPE dwGlobalIsr; /* mask used to enable/disable interrupts */
xTaskHandle hTask; /* Task handle so we know the name of this task */
BOOL yielded; /* Need to know how task went out of focus */
}SSIM_T;
#define portNO_CRITICAL_NESTING ( ( unsigned portLONG ) 0 )
#define DEBUG_OUTPUT
//#define DEBUG_OUTPUT
//#define ERROR_OUTPUT
#ifdef DEBUG_OUTPUT
#define debug_printf(...) ( (WaitForSingleObject(hPrintfMutex, INFINITE)|1)?( \
/* #define debug_printf(...) ( (WaitForSingleObject(hPrintfMutex, INFINITE)|1)?( \
( \
(NULL != (debug_task_handle = (tskTCB *) pxCurrentTCB ))? \
(fprintf( stderr, "%20s\t%20s\t%i: ",debug_task_handle->pcTaskName,__func__,__LINE__)): \
(fprintf( stderr, "%20s\t%20s\t%i: ","__unknown__",__func__,__LINE__)) \
|1)?( \
((fprintf( stderr, __VA_ARGS__ )|1)?ReleaseMutex( hPrintfMutex ):0) \
):0 ):0 )
):0 ):0 ) */
#define debug_printf(...) ( ( (NULL != (debug_task_handle = (tskTCB *) pxCurrentTCB ))? \
(fprintf( stderr, "%20s\t%20s\t%i: ",debug_task_handle->pcTaskName,__func__,__LINE__)): \
(fprintf( stderr, "%20s\t%20s\t%i: ","__unknown__",__func__,__LINE__)) \
|1)? \
fprintf( stderr, __VA_ARGS__ ) : 0 \
)
#define debug_error debug_printf
#else
#ifdef ERROR_OUTPUT
#define debug_error(...) ( (WaitForSingleObject(hPrintfMutex, INFINITE)|1)?( \
/* #define debug_error(...) ( (WaitForSingleObject(hPrintfMutex, INFINITE)|1)?( \
( \
(NULL != (debug_task_handle = (tskTCB *) pxCurrentTCB ))? \
(fprintf( stderr, "%20s\t%20s\t%i: ",debug_task_handle->pcTaskName,__func__,__LINE__)): \
(fprintf( stderr, "%20s\t%20s\t%i: ","__unknown__",__func__,__LINE__)) \
|1)?( \
((fprintf( stderr, __VA_ARGS__ )|1)?ReleaseMutex( hPrintfMutex ):0) \
):0 ):0 )
):0 ):0 )*/
#define debug_error(...) ( ( (NULL != (debug_task_handle = (tskTCB *) pxCurrentTCB ))? \
(fprintf( stderr, "%20s\t%20s\t%i: ",debug_task_handle->pcTaskName,__func__,__LINE__)): \
(fprintf( stderr, "%20s\t%20s\t%i: ","__unknown__",__func__,__LINE__)) \
|1)? \
fprintf( stderr, __VA_ARGS__ ) : 0 \
)
#define debug_printf(...)
#else
@ -150,10 +163,6 @@ typedef struct
volatile DWORD dwPendingIsr; // pending interrupts
HANDLE hIsrInvoke; // event to signal an interrupt
HANDLE hIsrMutex; // mutex to protect above 2
#if defined(DEBUG_OUTPUT) || defined(ERROR_OUTPUT)
HANDLE hPrintfMutex;// mutex for debug_printf() and debug_error()
#endif
SSIM_T *pxLastAddedTCB;
/******************************************************************************
National variables
@ -175,6 +184,7 @@ ESWI_ID;
unsigned portLONG ulCriticalNesting = ( unsigned portLONG ) 9999;
void (*vIsrHandler[CPU_INTR_COUNT])(void);
UINT msPerTick; //Returned from timeGetDevCaps()
#if portDEBUG == 1
@ -209,18 +219,26 @@ static DWORD WINAPI tick_generator(LPVOID lpParameter)
HANDLE hTimer;
LARGE_INTEGER liDueTime;
HANDLE hObjList[2];
float before, after;
hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
liDueTime.QuadPart = -20000; // 1ms interval
liDueTime.QuadPart = -(50000 - 10000*(int)msPerTick); // 5ms -
//there is always another tick during WaitForMultipleObjects() while waiting
//for the mutex, so reduce the wait time by 1 tick
hObjList[0] = hIsrMutex;
hObjList[1] = hTimer;
for(;;)
{
before = (float)clock()/CLOCKS_PER_SEC;
debug_printf("tick before, %f\n", before);
SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE);
WaitForMultipleObjects(2, hObjList, TRUE, INFINITE);
after = (float)clock()/CLOCKS_PER_SEC;
debug_printf("tick after, %f\n", after);
debug_printf("diff: %f\n", after - before);
// generate the tick interrupt
dwPendingIsr |= (1<<CPU_INTR_TICK);
SetEvent(hIsrInvoke);
@ -229,7 +247,6 @@ static DWORD WINAPI tick_generator(LPVOID lpParameter)
// overruns).
SignalObjectAndWait(hIsrMutex, hTickAck, INFINITE, FALSE);
}
return 0;
}
static DWORD WINAPI TaskSimThread( LPVOID lpParameter )
@ -259,20 +276,26 @@ static void create_system_objects(void)
hIsrInvoke = CreateEvent(NULL, FALSE, FALSE, NULL);
hTickAck = CreateEvent(NULL, FALSE, FALSE, NULL);
hTermAck = CreateEvent(NULL, FALSE, FALSE, NULL);
#if defined(DEBUG_OUTPUT) || defined(ERROR_OUTPUT)
hPrintfMutex = CreateMutex(NULL, FALSE, NULL);
#endif
dwEnabledIsr |= (1<<CPU_INTR_TICK);
//Set timer
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(tc));
msPerTick = tc.wPeriodMin;
debug_printf("Ms per tick: %i\n", msPerTick);
if(msPerTick > 2)
{
printf("Warning: your system timer has a low resolution.\n");
printf("Either decrease the tick rate, or get a better PC!\n");
}
timeBeginPeriod(tc.wPeriodMin);
#if configUSE_PREEMPTION != 0
SetThreadPriority(CreateThread(NULL, 0, tick_generator, NULL, 0, NULL),
THREAD_PRIORITY_ABOVE_NORMAL);
#endif
//printf("got here!\n");
//printf("%i\n", (int) pxCurrentTCB);
//debug_printf("created system objects\n");
//printf("got here again!\n");
}
/******************************************************************************
@ -294,14 +317,13 @@ portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE
psSim->yielded = FALSE;
psSim->hThread = CreateThread(NULL, 0, TaskSimThread, psSim, CREATE_SUSPENDED, NULL);
ok=SetThreadPriority(psSim->hThread, THREAD_PRIORITY_IDLE);
pxLastAddedTCB = psSim;
return (portSTACK_TYPE *) psSim;
}
portBASE_TYPE xPortStartScheduler( void )
{
BOOL bSwitch;
SSIM_T *psSim, *psSimOld;
SSIM_T *psSim;
DWORD dwIntr;
int i, iIsrCount;
HANDLE hObjList[2];
@ -363,6 +385,7 @@ portBASE_TYPE xPortStartScheduler( void )
SuspendThread(psSim->hThread);
vTaskIncrementTick();
debug_printf("Sending tick ack...\n");
SetEvent(hTickAck);
break;
@ -378,33 +401,17 @@ portBASE_TYPE xPortStartScheduler( void )
if(bSwitch)
{
psSimOld = psSim;
vTaskSwitchContext();
debug_printf("switching context\n");
psSim=(SSIM_T *)*pxCurrentTCB;
if(psSimOld != psSim)
{
ulCriticalNesting = psSim->ulCriticalNesting;
dwGlobalIsr = psSim->dwGlobalIsr;
ulCriticalNesting = psSim->ulCriticalNesting;
dwGlobalIsr = psSim->dwGlobalIsr;
if(psSim->yielded) {
psSim->yielded = FALSE;
ReleaseSemaphore(psSim->hSemaphore, 1, NULL); // awake next task
} else {
ResumeThread(psSim->hThread);
}
debug_printf("switched context\n");
}
else
{
//Oops, we just suspended the task that we want to resume!
//TODO: resolve this before it happens
if(psSim->yielded) {
psSim->yielded = FALSE;
ReleaseSemaphore(psSim->hSemaphore, 1, NULL); // awake next task
} else {
ResumeThread(psSim->hThread);
}
debug_printf("didn't switch context\n");
if(psSim->yielded) {
psSim->yielded = FALSE;
ReleaseSemaphore(psSim->hSemaphore, 1, NULL); // awake next task
} else {
ResumeThread(psSim->hThread);
}
}
@ -493,6 +500,8 @@ int iPortSetIsrHandler(int iNo, void (*handler)(void))
{
if(iNo < CPU_INTR_COUNT) {
if(hIsrDispatcher) {
//SSIM_T *psSim=(SSIM_T *)*pxCurrentTCB;
WaitForSingleObject(hIsrMutex, INFINITE);
vIsrHandler[iNo]=handler;
ReleaseMutex(hIsrMutex);
@ -586,10 +595,3 @@ void vPortExitCritical( void )
}
}
}
void vPortAddTaskHandle( void *pxTaskHandle )
{
//printf("got here!\n");
//printf("%i\n", (int)pxLastAddedTCB);
pxLastAddedTCB->hTask = (xTaskHandle)pxTaskHandle;
}

View File

@ -1,188 +0,0 @@
File: port.c.documentation.txt
Author: Corvus Corax
Desc: Description of port.c Functions and Directions about porting.
See FreeRTOS documentation ebook for details.
FreeRTOS is an architecture independent real time operating system.
Most architecture dependant code sits in a single file: port.c
Architecture dependant definitions sit in: portmacro.h
Other important files:
Source/portable/MemMang/head_3.c - memory management - very simple. Provides
functions like malloc and free - very easy to make a wrapper for on any system
that provides memory management.
FreeRTOS has internal scheduling. The real time scheduler sits in Source/task.c and calls low level functions of port.c for thread management.
For that port.c needs to provide functions to switch between threads on
request, as well as a tick handler that preempts threads on a periodic basis.
Only one FreeRTOS thread is active at any time!
port.c provides the API defined in portmacro.h.
Only a subset of the functions is explained here (with the naming from the
posix port. Their macros are sometimes named a bit different)
void vPortEnterCritical(void);
This function is called if a thread enters a "critical section".
In a critical sections the thread must not be preempted.
(To preempt a thread means to halt its execution when a timer interrupt comes
in, and give execution to another thread)
This function should increase a counter for that thread, since several
"Critical Sections" could be cascaded. Only if the outermost critical section
is exited, is preemtion allowed again.
void vPortExitCritical(void);
This function is called if a thread leaves a "critical section".
If a thread leaves the outermost critical section, the scheduler is allowed to
preempt it on timer interrupt (or other interrupts)
void vPortEnableInterrupts(void);
void vPortDisableInterrupts(void);
functions to enable and disable interrupts. On "real systems" this means all
interrupts including IO. When "simulating" this means the tick handler/ timer
/ timer interrupt. The tick handler is supposed to not do anything if interrupts are disabled.
portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack,
pdTASK_CODE pxCode, void *pvParameters );
Used to initialize a new thread. The stack memory area, command line
parameters for this task and the entry function (pxCode) are given
This function needs to initialize the new task/thread, but suspend it
immediately. It is only to be executed later if the scheduler says so.
returns pxTopOfStack if success and 0 on failure.
THIS WILL BE THE FIRST FUNCTION FreeRTOS CALLS.
The first thread to be created is likely the idle thread. At this time the
scheduler has not been started yet. Therefore it's important to start all
threads in suspended state!
void vPortEndScheduler(void);
Needs to end the scheduler (and as such all Tasks/threads) This means FreeRTOS
terminates - as in (simulated) system shutdown.
portBASE_TYPE xPortStartScheduler(void);
This function doesn't return until someone (another thread) calls
vPortEndScheduler(). You can set up your timer and tick handler here and start
the first thread. Then do what a scheduler does (manage threads)
vPortYield()
Sometimes threads go sleeping on purpose (for example during one of FreeRTOS
system calls - including sleep() )
This function should send the thread that calls it into suspended state and
not return until the scheduler gives back execution to this thread.
===========
The scheduler.
What your scheduler needs to do:
Basically what your self written scheduler is allowed to do is the "dirty
work" for the high level scheduler logic provided by FreeRTOS task.c
The scheduler is supposed to run a timer interrupt/tick handler that gets
called every portTICK_RATE_MICROSECONDS microseconds.
That value is defined somewhere in OpenPilot and has to be exact as well as
the same on all architectures.
If you cannot set up a timer with that accuracy you are screwed!
Anyway. Every time the timer tick happens, you have to
- check whether you are allowed to execute the tick handler.
If interrupts are disabled and/or the thread is in a critical section, the
tick handler should do nothing
- Tell FreeRTOS that the tick has happened
Increment the Tick Count using the FreeRTOS function
vTaskIncrementTick();
- If preemption is enabled (and in OpenPilot it is!)
Tell the high level Scheduler of FreeRTOS to do its magic, using the
function
vTaskSwitchContext();
- You can find out which thread is SUPPOSED to be running with the function
xTaskGetCurrentTaskHandle();
- If this is for some reason not the currently running thread, SUSPEND that
thread with whatever method possible (signals, events, operating system
thread.suspend() - I don't know how to do that in Qt.
- Make the thread returned by xTaskGetCurrentTaskHandle() resume operation as
normal.
- Make sure that when you return from the tick handler, exactly one thread is
running and that is the one by xTaskGetCurrentTaskHandle() and all others
are suspended!
On top of that, threads can suspend themselves just like that. That happens
every time they call any blocking FreeRTOS function.
They do that with above mentioned function
vPortYield()
When vPortYield is called your scheduler must:
- Tell the high level Scheduler of FreeRTOS to do its magic, using the
function
vTaskSwitchContext();
- You can then find out which thread is SUPPOSED to be running with the function
xTaskGetCurrentTaskHandle();
- Make sure that the thread calling this function SUSPENDS and the thread
returned by xTaskGetCurrentTaskHandle() gets executed. Be aware that they
could be the same in which case vPortYield does exactly NOTHING!
- This function does not return (since the current thread is sent to sleep)
until the scheduler makes it wake up - either by the tick handler, or by
another thread calling vPortYield().
- So it must not ever return until xTaskGetCurrentTaskHandle() says the
calling thread is the current task handle.
- Then it returns to the caller.
=====
What emthod you use to send threads/tasks to sleep and wake them up again is
up to you.
The posix implementation uses signals and a signal handler in each thread that
sleeps until a resume signal is received
The native STM32 implementation manually switches contexts by and uses actual
system interrupts
(so does the native x86 implementation)
The native Win32 implementation uses win32 API calls to manipulate windows
threads (windows actually provides a call to remote-suspend and resume a
thread - posix doesn't)
I have no clue what measures for thread control and suspension/interruption Qt
offers. (I hope there are some)

View File

@ -56,6 +56,7 @@
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <time.h>
#include "cpuemu.h"
#ifdef __cplusplus
@ -92,8 +93,8 @@ extern "C" {
/* Hardware specifics. */
#define portSTACK_GROWTH ( -1 )
#define portTICK_RATE_MS ( 1) //( ( portTickType ) 1000 / 15625 )
#define portMS_TO_TICKS(ms) ( ( portTickType ) (1000*(ms) + (15625)/2)/15625 )
#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ )
#define portTICK_RATE_MICROSECONDS ( ( portTickType ) 1000000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 4
#define portYIELD() __swi();
@ -126,8 +127,7 @@ extern void vTaskSwitchContext( void ); \
extern void vPortForciblyEndThread( void *pxTaskToDelete );
#define traceTASK_DELETE( pxTaskToDelete ) vPortForciblyEndThread( pxTaskToDelete )
extern void vPortAddTaskHandle( void *pxTaskHandle );
#define traceTASK_CREATE( pxNewTCB ) vPortAddTaskHandle( pxNewTCB )
#define traceTASK_CREATE( pxNewTCB )
/* Make use of times(man 2) to gather run-time statistics on the tasks. */
extern void vPortFindTicksPerSecond( void );