From d2311161540192cbc06d5bb19f3767bfc80274c2 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Tue, 5 Feb 2008 23:08:51 -0700 Subject: [PATCH] Imported upstream version 1.20 --- FreeRTOS/Makefile | 29 + FreeRTOS/croutine.c | 345 +++ FreeRTOS/include/FreeRTOS.h | 114 + FreeRTOS/include/croutine.h | 716 +++++ FreeRTOS/include/list.h | 282 ++ FreeRTOS/include/portable.h | 239 ++ FreeRTOS/include/projdefs.h | 59 + FreeRTOS/include/queue.h | 474 ++++ FreeRTOS/include/semphr.h | 292 ++ FreeRTOS/include/task.h | 970 +++++++ FreeRTOS/list.c | 204 ++ FreeRTOS/portable/GCC/ARM7_LPC2000/Makefile | 25 + FreeRTOS/portable/GCC/ARM7_LPC2000/port.c | 245 ++ FreeRTOS/portable/GCC/ARM7_LPC2000/portISR.c | 238 ++ .../portable/GCC/ARM7_LPC2000/portmacro.h | 267 ++ FreeRTOS/portable/GCC/Makefile | 9 + FreeRTOS/portable/Makefile | 9 + FreeRTOS/portable/MemMang/Makefile | 25 + FreeRTOS/portable/MemMang/heap_1.c | 140 + FreeRTOS/portable/MemMang/heap_2.c | 271 ++ FreeRTOS/portable/MemMang/heap_3.c | 82 + FreeRTOS/queue.c | 930 +++++++ FreeRTOS/tasks.c | 1936 +++++++++++++ FreeRTOSConfig.h | 76 + Makefile | 81 + README | 741 +++++ Windows/usbser.inf | 45 + Windows/usbser.sys | Bin 0 -> 22766 bytes adc/Makefile | 25 + adc/adc.c | 28 + adc/adc.h | 7 + boot.s | 367 +++ cpu/Makefile | 25 + cpu/cpu.c | 132 + cpu/cpu.h | 10 + dac/Makefile | 25 + dac/dac.c | 26 + dac/dac.h | 7 + eints/Makefile | 25 + eints/eints.c | 30 + eints/eints.h | 6 + eints/eintsISR.c | 42 + eints/eintsISR.h | 7 + fatfs/Makefile | 25 + fatfs/disk.h | 73 + fatfs/diskio.h | 61 + fatfs/ff.c | 1841 +++++++++++++ fatfs/ff.h | 338 +++ fatfs/mmc.c | 944 +++++++ fatfs/mmc.h | 127 + fatfs/spi.c | 284 ++ fatfs/spi.h | 20 + fiq/Makefile | 25 + fiq/fiq.c | 59 + fiq/fiq.h | 11 + gps/Makefile | 25 + gps/gps.c | 284 ++ gps/gps.h | 33 + i2c/Makefile | 25 + i2c/eeprom.c | 149 + i2c/eeprom.h | 23 + i2c/eeprom_working.c | 156 ++ i2c/i2c.c | 60 + i2c/i2c.h | 58 + i2c/i2cInt.c | 405 +++ i2c/i2cIntWorking.c | 342 +++ i2c/i2cPolled.c | 412 +++ i2c/lm75.c | 158 ++ i2c/lm75.h | 29 + iap/Makefile | 25 + iap/iap.c | 537 ++++ iap/iap.h | 32 + leds/Makefile | 25 + leds/leds.c | 77 + leds/leds.h | 14 + leds/leds_reuse.c | 58 + lpc210x.h | 2310 ++++++++++++++++ lpc2148-rom.ld | 70 + lpc2148_demo.fms | 183 ++ main.c | 87 + main.h | 24 + monitor/Makefile | 25 + monitor/args.c | 167 ++ monitor/args.h | 7 + monitor/monitor.c | 2426 +++++++++++++++++ monitor/monitor.h | 6 + newlib/Makefile | 25 + newlib/syscalls.c | 732 +++++ rtc/Makefile | 25 + rtc/rtc.c | 268 ++ rtc/rtc.h | 23 + rtc/rtcISR.c | 75 + rtc/rtcISR.h | 8 + sensors/Makefile | 25 + sensors/sensors.c | 94 + sensors/sensors.h | 22 + swi/Makefile | 31 + swi/swi.c | 73 + swi/swi.h | 60 + swi/swidispatch.s | 131 + sysdefs.h | 52 + uart/Makefile | 25 + uart/uart.c | 350 +++ uart/uart.h | 17 + uart/uartISR.c | 193 ++ uart/uartISR.h | 16 + usb/Makefile | 25 + usb/usbISR.c | 411 +++ usb/usbISR.h | 98 + usb/usbapi.h | 92 + usb/usbcontrol.c | 173 ++ usb/usbinit.c | 30 + usb/usbstdreq.c | 349 +++ usb/usbstruct.h | 127 + usbmass/Makefile | 25 + usbmass/mscblock.c | 367 +++ usbmass/mscblock.h | 39 + usbmass/mscbot.c | 443 +++ usbmass/mscbot.h | 40 + usbmass/mscdebug.h | 41 + usbmass/mscscsi.c | 334 +++ usbmass/mscscsi.h | 37 + usbmass/mscspi.c | 82 + usbmass/mscspi.h | 49 + usbmass/mscspi2.c | 82 + usbmass/usbmass.c | 205 ++ usbmass/usbmass.h | 6 + usbser/Makefile | 25 + usbser/usbser.c | 338 +++ usbser/usbser.h | 16 + 130 files changed, 27325 insertions(+) create mode 100644 FreeRTOS/Makefile create mode 100644 FreeRTOS/croutine.c create mode 100644 FreeRTOS/include/FreeRTOS.h create mode 100644 FreeRTOS/include/croutine.h create mode 100644 FreeRTOS/include/list.h create mode 100644 FreeRTOS/include/portable.h create mode 100644 FreeRTOS/include/projdefs.h create mode 100644 FreeRTOS/include/queue.h create mode 100644 FreeRTOS/include/semphr.h create mode 100644 FreeRTOS/include/task.h create mode 100644 FreeRTOS/list.c create mode 100644 FreeRTOS/portable/GCC/ARM7_LPC2000/Makefile create mode 100644 FreeRTOS/portable/GCC/ARM7_LPC2000/port.c create mode 100644 FreeRTOS/portable/GCC/ARM7_LPC2000/portISR.c create mode 100644 FreeRTOS/portable/GCC/ARM7_LPC2000/portmacro.h create mode 100644 FreeRTOS/portable/GCC/Makefile create mode 100644 FreeRTOS/portable/Makefile create mode 100644 FreeRTOS/portable/MemMang/Makefile create mode 100644 FreeRTOS/portable/MemMang/heap_1.c create mode 100644 FreeRTOS/portable/MemMang/heap_2.c create mode 100644 FreeRTOS/portable/MemMang/heap_3.c create mode 100644 FreeRTOS/queue.c create mode 100644 FreeRTOS/tasks.c create mode 100644 FreeRTOSConfig.h create mode 100644 Makefile create mode 100644 README create mode 100644 Windows/usbser.inf create mode 100755 Windows/usbser.sys create mode 100644 adc/Makefile create mode 100644 adc/adc.c create mode 100644 adc/adc.h create mode 100644 boot.s create mode 100644 cpu/Makefile create mode 100644 cpu/cpu.c create mode 100644 cpu/cpu.h create mode 100644 dac/Makefile create mode 100644 dac/dac.c create mode 100644 dac/dac.h create mode 100644 eints/Makefile create mode 100644 eints/eints.c create mode 100644 eints/eints.h create mode 100644 eints/eintsISR.c create mode 100644 eints/eintsISR.h create mode 100644 fatfs/Makefile create mode 100644 fatfs/disk.h create mode 100644 fatfs/diskio.h create mode 100644 fatfs/ff.c create mode 100644 fatfs/ff.h create mode 100644 fatfs/mmc.c create mode 100644 fatfs/mmc.h create mode 100644 fatfs/spi.c create mode 100644 fatfs/spi.h create mode 100644 fiq/Makefile create mode 100644 fiq/fiq.c create mode 100644 fiq/fiq.h create mode 100644 gps/Makefile create mode 100644 gps/gps.c create mode 100644 gps/gps.h create mode 100644 i2c/Makefile create mode 100644 i2c/eeprom.c create mode 100644 i2c/eeprom.h create mode 100644 i2c/eeprom_working.c create mode 100644 i2c/i2c.c create mode 100644 i2c/i2c.h create mode 100644 i2c/i2cInt.c create mode 100644 i2c/i2cIntWorking.c create mode 100644 i2c/i2cPolled.c create mode 100644 i2c/lm75.c create mode 100644 i2c/lm75.h create mode 100644 iap/Makefile create mode 100644 iap/iap.c create mode 100644 iap/iap.h create mode 100644 leds/Makefile create mode 100644 leds/leds.c create mode 100644 leds/leds.h create mode 100644 leds/leds_reuse.c create mode 100644 lpc210x.h create mode 100644 lpc2148-rom.ld create mode 100755 lpc2148_demo.fms create mode 100644 main.c create mode 100644 main.h create mode 100644 monitor/Makefile create mode 100644 monitor/args.c create mode 100644 monitor/args.h create mode 100644 monitor/monitor.c create mode 100644 monitor/monitor.h create mode 100644 newlib/Makefile create mode 100644 newlib/syscalls.c create mode 100644 rtc/Makefile create mode 100644 rtc/rtc.c create mode 100644 rtc/rtc.h create mode 100644 rtc/rtcISR.c create mode 100644 rtc/rtcISR.h create mode 100644 sensors/Makefile create mode 100644 sensors/sensors.c create mode 100644 sensors/sensors.h create mode 100644 swi/Makefile create mode 100644 swi/swi.c create mode 100644 swi/swi.h create mode 100644 swi/swidispatch.s create mode 100644 sysdefs.h create mode 100644 uart/Makefile create mode 100644 uart/uart.c create mode 100644 uart/uart.h create mode 100644 uart/uartISR.c create mode 100644 uart/uartISR.h create mode 100644 usb/Makefile create mode 100644 usb/usbISR.c create mode 100644 usb/usbISR.h create mode 100644 usb/usbapi.h create mode 100644 usb/usbcontrol.c create mode 100644 usb/usbinit.c create mode 100644 usb/usbstdreq.c create mode 100644 usb/usbstruct.h create mode 100644 usbmass/Makefile create mode 100644 usbmass/mscblock.c create mode 100644 usbmass/mscblock.h create mode 100644 usbmass/mscbot.c create mode 100644 usbmass/mscbot.h create mode 100644 usbmass/mscdebug.h create mode 100644 usbmass/mscscsi.c create mode 100644 usbmass/mscscsi.h create mode 100644 usbmass/mscspi.c create mode 100644 usbmass/mscspi.h create mode 100644 usbmass/mscspi2.c create mode 100644 usbmass/usbmass.c create mode 100644 usbmass/usbmass.h create mode 100644 usbser/Makefile create mode 100644 usbser/usbser.c create mode 100644 usbser/usbser.h diff --git a/FreeRTOS/Makefile b/FreeRTOS/Makefile new file mode 100644 index 0000000..03f281c --- /dev/null +++ b/FreeRTOS/Makefile @@ -0,0 +1,29 @@ +SRC_FILES=croutine.c list.c queue.c tasks.c + +SUBDIRS=portable + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + @for i in $(SUBDIRS); do \ + (cd $$i; $(MAKE) $(MFLAGS) $(MYMAKEFLAGS) all); done + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/FreeRTOS/croutine.c b/FreeRTOS/croutine.c new file mode 100644 index 0000000..4f2ba87 --- /dev/null +++ b/FreeRTOS/croutine.c @@ -0,0 +1,345 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +#include "FreeRTOS.h" +#include "task.h" +#include "croutine.h" + +/* Lists for ready and blocked co-routines. --------------------*/ +static xList pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */ +static xList xDelayedCoRoutineList1; /*< Delayed co-routines. */ +static xList xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */ +static xList * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */ +static xList * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */ +static xList xPendingReadyList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */ + +/* Other file private variables. --------------------------------*/ +corCRCB * pxCurrentCoRoutine = NULL; +static unsigned portBASE_TYPE uxTopCoRoutineReadyPriority = 0; +static portTickType xCoRoutineTickCount = 0; + +/* The initial state of the co-routine when it is created. */ +#define corINITIAL_STATE ( 0 ) + +/* + * Place the co-routine represented by pxCRCB into the appropriate ready queue + * for the priority. It is inserted at the end of the list. + * + * This macro accesses the co-routine ready lists and therefore must not be + * used from within an ISR. + */ +#define prvAddCoRoutineToReadyQueue( pxCRCB ) \ +{ \ + if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \ + { \ + uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \ + } \ + vListInsertEnd( ( xList * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \ +} + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first co-routine. + */ +static void prvInitialiseCoRoutineLists( void ); + +/* + * Co-routines that are readied by an interrupt cannot be placed directly into + * the ready lists (there is no mutual exclusion). Instead they are placed in + * in the pending ready list in order that they can later be moved to the ready + * list by the co-routine scheduler. + */ +static inline void prvCheckPendingReadyList( void ); + +/* + * Macro that looks at the list of co-routines that are currently delayed to + * see if any require waking. + * + * Co-routines are stored in the queue in the order of their wake time - + * meaning once one co-routine has been found whose timer has not expired + * we need not look any further down the list. + */ +static inline void prvCheckDelayedList( void ); + +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ) +{ +signed portBASE_TYPE xReturn; +corCRCB *pxCoRoutine; + + /* Allocate the memory that will store the co-routine control block. */ + pxCoRoutine = ( corCRCB * ) pvPortMalloc( sizeof( corCRCB ) ); + if( pxCoRoutine ) + { + /* If pxCurrentCoRoutine is NULL then this is the first co-routine to + be created and the co-routine data structures need initialising. */ + if( pxCurrentCoRoutine == NULL ) + { + pxCurrentCoRoutine = pxCoRoutine; + prvInitialiseCoRoutineLists(); + } + + /* Check the priority is within limits. */ + if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES ) + { + uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1; + } + + /* Fill out the co-routine control block from the function parameters. */ + pxCoRoutine->uxState = corINITIAL_STATE; + pxCoRoutine->uxPriority = uxPriority; + pxCoRoutine->uxIndex = uxIndex; + pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode; + + /* Initialise all the other co-routine control block parameters. */ + vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) ); + vListInitialiseItem( &( pxCoRoutine->xEventListItem ) ); + + /* Set the co-routine control block as a link back from the xListItem. + This is so we can get back to the containing CRCB from a generic item + in a list. */ + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine ); + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority ); + + /* Now the co-routine has been initialised it can be added to the ready + list at the correct priority. */ + prvAddCoRoutineToReadyQueue( pxCoRoutine ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineAddToDelayedList( portTickType xTicksToDelay, xList *pxEventList ) +{ +portTickType xTimeToWake; + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xCoRoutineTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xCoRoutineTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedCoRoutineList, ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedCoRoutineList, ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + + if( pxEventList ) + { + /* Also add the co-routine to an event list. If this is done then the + function must be called with interrupts disabled. */ + vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) ); + } +} +/*-----------------------------------------------------------*/ + +static inline void prvCheckPendingReadyList( void ) +{ + /* Are there any co-routines waiting to get moved to the ready list? These + are co-routines that have been readied by an ISR. The ISR cannot access + the ready lists itself. */ + while( !listLIST_IS_EMPTY( &xPendingReadyList ) ) + { + corCRCB *pxUnblockedCRCB; + + /* The pending ready list can be accessed by an ISR. */ + portDISABLE_INTERRUPTS(); + { + pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyList) ); + vListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + } + portENABLE_INTERRUPTS(); + + vListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); + prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); + } +} +/*-----------------------------------------------------------*/ + +static inline void prvCheckDelayedList( void ) +{ +static portTickType xLastTickCount, xPassedTicks; +corCRCB *pxCRCB; + + xPassedTicks = xTaskGetTickCount() - xLastTickCount; + while( xPassedTicks ) + { + xCoRoutineTickCount++; + xPassedTicks--; + + /* If the tick count has overflowed we need to swap the ready lists. */ + if( xCoRoutineTickCount == 0 ) + { + xList * pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. If there are + any items in pxDelayedCoRoutineList here then there is an error! */ + pxTemp = pxDelayedCoRoutineList; + pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList; + pxOverflowDelayedCoRoutineList = pxTemp; + } + + /* See if this tick has made a timeout expire. */ + while( ( pxCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList ) ) != NULL ) + { + if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) ) + { + /* Timeout not yet expired. */ + break; + } + + portDISABLE_INTERRUPTS(); + { + /* The event could have occurred just before this critical + section. If this is the case then the generic list item will + have been moved to the pending ready list and the following + line is still valid. Also the pvContainer parameter will have + been set to NULL so the following lines are also valid. */ + vListRemove( &( pxCRCB->xGenericListItem ) ); + + /* Is the co-routine waiting on an event also? */ + if( pxCRCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxCRCB->xEventListItem ) ); + } + } + portENABLE_INTERRUPTS(); + + prvAddCoRoutineToReadyQueue( pxCRCB ); + } + } + + xLastTickCount = xCoRoutineTickCount; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineSchedule( void ) +{ + /* See if any co-routines readied by events need moving to the ready lists. */ + prvCheckPendingReadyList(); + + /* See if any delayed co-routines have timed out. */ + prvCheckDelayedList(); + + /* Find the highest priority queue that contains ready co-routines. */ + while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) ) + { + if( uxTopCoRoutineReadyPriority == 0 ) + { + /* No more co-routines to check. */ + return; + } + --uxTopCoRoutineReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines + of the same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ); + + /* Call the co-routine. */ + ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex ); + + return; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseCoRoutineLists( void ) +{ +unsigned portBASE_TYPE uxPriority; + + for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( xList * ) &( pxReadyCoRoutineLists[ uxPriority ] ) ); + } + + vListInitialise( ( xList * ) &xDelayedCoRoutineList1 ); + vListInitialise( ( xList * ) &xDelayedCoRoutineList2 ); + vListInitialise( ( xList * ) &xPendingReadyList ); + + /* Start with pxDelayedCoRoutineList using list1 and the + pxOverflowDelayedCoRoutineList using list2. */ + pxDelayedCoRoutineList = &xDelayedCoRoutineList1; + pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xCoRoutineRemoveFromEventList( const xList *pxEventList ) +{ +corCRCB *pxUnblockedCRCB; +signed portBASE_TYPE xReturn; + + /* This function is called from within an interrupt. It can only access + event lists and the pending ready list. */ + pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + vListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxUnblockedCRCB->xEventListItem ) ); + + if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} + diff --git a/FreeRTOS/include/FreeRTOS.h b/FreeRTOS/include/FreeRTOS.h new file mode 100644 index 0000000..676bda3 --- /dev/null +++ b/FreeRTOS/include/FreeRTOS.h @@ -0,0 +1,114 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +#ifndef INC_FREERTOS_H +#define INC_FREERTOS_H + + +/* + * Include the generic headers required for the FreeRTOS port being used. + */ +#include + +/* Basic FreeRTOS definitions. */ +#include "projdefs.h" + +/* Application specific configuration options. */ +#include "FreeRTOSConfig.h" + +/* Definitions specific to the port being used. */ +#include "portable.h" + + + + + + + +/* + * Check all the required application specific macros have been defined. + * These macros are application specific and (as downloaded) are defined + * within FreeRTOSConfig.h. + */ + +#ifndef configUSE_PREEMPTION + #error Missing definition: configUSE_PREEMPTION should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_IDLE_HOOK + #error Missing definition: configUSE_IDLE_HOOK should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_TICK_HOOK + #error Missing definition: configUSE_TICK_HOOK should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_CO_ROUTINES + #error Missing definition: configUSE_CO_ROUTINES should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskPrioritySet + #error Missing definition: INCLUDE_vTaskPrioritySet should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_uxTaskPriorityGet + #error Missing definition: INCLUDE_uxTaskPriorityGet should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelete + #error Missing definition: INCLUDE_vTaskDelete should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskCleanUpResources + #error Missing definition: INCLUDE_vTaskCleanUpResources should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskSuspend + #error Missing definition: INCLUDE_vTaskSuspend should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelayUntil + #error Missing definition: INCLUDE_vTaskDelayUntil should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelay + #error Missing definition: INCLUDE_vTaskDelay should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_16_BIT_TICKS + #error Missing definition: configUSE_16_BIT_TICKS should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#endif diff --git a/FreeRTOS/include/croutine.h b/FreeRTOS/include/croutine.h new file mode 100644 index 0000000..d3b5216 --- /dev/null +++ b/FreeRTOS/include/croutine.h @@ -0,0 +1,716 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ +#ifndef CO_ROUTINE_H +#define CO_ROUTINE_H + +#include "list.h" + +/* Used to hide the implementation of the co-routine control block. The +control block structure however has to be included in the header due to +the macro implementation of the co-routine functionality. */ +typedef void * xCoRoutineHandle; + +/* Defines the prototype to which co-routine functions must conform. */ +typedef void (*crCOROUTINE_CODE)( xCoRoutineHandle, unsigned portBASE_TYPE ); + +typedef struct corCoRoutineControlBlock +{ + crCOROUTINE_CODE pxCoRoutineFunction; + xListItem xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */ + xListItem xEventListItem; /*< List item used to place the CRCB in event lists. */ + unsigned portBASE_TYPE uxPriority; /*< The priority of the co-routine in relation to other co-routines. */ + unsigned portBASE_TYPE uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */ + unsigned portSHORT uxState; /*< Used internally by the co-routine implementation. */ +} corCRCB; /* Co-routine control block. Note must be identical in size down to uxPriority with tskTCB. */ + +/** + * croutine. h + *
+ portBASE_TYPE xCoRoutineCreate(
+                                 crCOROUTINE_CODE pxCoRoutineCode,
+                                 unsigned portBASE_TYPE uxPriority,
+                                 unsigned portBASE_TYPE uxIndex
+                               );
+ * + * Create a new co-routine and add it to the list of co-routines that are + * ready to run. + * + * @param pxCoRoutineCode Pointer to the co-routine function. Co-routine + * functions require special syntax - see the co-routine section of the WEB + * documentation for more information. + * + * @param uxPriority The priority with respect to other co-routines at which + * the co-routine will run. + * + * @param uxIndex Used to distinguish between different co-routines that + * execute the same function. See the example below and the co-routine section + * of the WEB documentation for further information. + * + * @return pdPASS if the co-routine was successfully created and added to a ready + * list, otherwise an error code defined with ProjDefs.h. + * + * Example usage: +
+ // Co-routine to be created.
+ void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ static const char cLedToFlash[ 2 ] = { 5, 6 };
+ static const portTickType xTimeToDelay[ 2 ] = { 200, 400 };
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // This co-routine just delays for a fixed period, then toggles
+         // an LED.  Two co-routines are created using this function, so
+         // the uxIndex parameter is used to tell the co-routine which
+         // LED to flash and how long to delay.  This assumes xQueue has
+         // already been created.
+         vParTestToggleLED( cLedToFlash[ uxIndex ] );
+         crDELAY( xHandle, uxFlashRates[ uxIndex ] );
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+
+ // Function that creates two co-routines.
+ void vOtherFunction( void )
+ {
+ unsigned char ucParameterToPass;
+ xTaskHandle xHandle;
+		
+     // Create two co-routines at priority 0.  The first is given index 0
+     // so (from the code above) toggles LED 5 every 200 ticks.  The second
+     // is given index 1 so toggles LED 6 every 400 ticks.
+     for( uxIndex = 0; uxIndex < 2; uxIndex++ )
+     {
+         xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
+     }
+ }
+   
+ * \defgroup xCoRoutineCreate xCoRoutineCreate + * \ingroup Tasks + */ +signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ); + + +/** + * croutine. h + *
+ void vCoRoutineSchedule( void );
+ * + * Run a co-routine. + * + * vCoRoutineSchedule() executes the highest priority co-routine that is able + * to run. The co-routine will execute until it either blocks, yields or is + * preempted by a task. Co-routines execute cooperatively so one + * co-routine cannot be preempted by another, but can be preempted by a task. + * + * If an application comprises of both tasks and co-routines then + * vCoRoutineSchedule should be called from the idle task (in an idle task + * hook). + * + * Example usage: +
+ // This idle task hook will schedule a co-routine each time it is called.
+ // The rest of the idle task will execute between co-routine calls.
+ void vApplicationIdleHook( void )
+ {
+	vCoRoutineSchedule();
+ }
+
+ // Alternatively, if you do not require any other part of the idle task to
+ // execute, the idle task hook can call vCoRoutineScheduler() within an
+ // infinite loop.
+ void vApplicationIdleHook( void )
+ {
+    for( ;; )
+    {
+        vCoRoutineSchedule();
+    }
+ }
+ 
+ * \defgroup vCoRoutineSchedule vCoRoutineSchedule + * \ingroup Tasks + */ +void vCoRoutineSchedule( void ); + +/** + * croutine. h + *
+ crSTART( xCoRoutineHandle xHandle );
+ * + * This macro MUST always be called at the start of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portLONG ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crSTART( pxCRCB ) switch( ( ( corCRCB * )pxCRCB )->uxState ) { case 0: + +/** + * croutine. h + *
+ crEND();
+ * + * This macro MUST always be called at the end of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portLONG ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crEND() } + +/* + * These macros are intended for internal use by the co-routine implementation + * only. The macros should not be used directly by application writers. + */ +#define crSET_STATE0( xHandle ) ( ( corCRCB * )xHandle)->uxState = (__LINE__ * 2); return; case (__LINE__ * 2): +#define crSET_STATE1( xHandle ) ( ( corCRCB * )xHandle)->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1): + +/** + * croutine. h + *
+ crDELAY( xCoRoutineHandle xHandle, portTickType xTicksToDelay );
+ * + * Delay a co-routine for a fixed period of time. + * + * crDELAY can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * @param xHandle The handle of the co-routine to delay. This is the xHandle + * parameter of the co-routine function. + * + * @param xTickToDelay The number of ticks that the co-routine should delay + * for. The actual amount of time this equates to is defined by + * configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_RATE_MS + * can be used to convert ticks to milliseconds. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ // We are to delay for 200ms.
+ static const xTickType xDelayTime = 200 / portTICK_RATE_MS;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+        // Delay for 200ms.
+        crDELAY( xHandle, xDelayTime );
+
+        // Do something here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crDELAY crDELAY + * \ingroup Tasks + */ +#define crDELAY( xHandle, xTicksToDelay ) \ + if( xTicksToDelay > 0 ) \ + { \ + vCoRoutineAddToDelayedList( xTicksToDelay, NULL ); \ + } \ + crSET_STATE0( xHandle ); + +/** + *
+ crQUEUE_SEND(
+                  xCoRoutineHandle xHandle,
+                  xQueueHandle pxQueue,
+                  void *pvItemToQueue,
+                  portTickType xTicksToWait,
+                  portBASE_TYPE *pxResult
+             )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_SEND can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue on which the data will be posted. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvItemToQueue A pointer to the data being posted onto the queue. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied from pvItemToQueue into the queue + * itself. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for space to become available on the queue, should space not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_RATE_MS can be used to convert ticks to milliseconds (see example + * below). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully posted onto the queue, otherwise it will be set to an + * error defined within ProjDefs.h. + * + * Example usage: +
+ // Co-routine function that blocks for a fixed period then posts a number onto
+ // a queue.
+ static void prvCoRoutineFlashTask( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portBASE_TYPE xNumberToPost = 0;
+ static portBASE_TYPE xResult;
+
+    // Co-routines must begin with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // This assumes the queue has already been created.
+        crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
+
+        if( xResult != pdPASS )
+        {
+            // The message was not posted!
+        }
+
+        // Increment the number to be posted onto the queue.
+        xNumberToPost++;
+
+        // Delay for 100 ticks.
+        crDELAY( xHandle, 100 );
+    }
+
+    // Co-routines must end with a call to crEND().
+    crEND();
+ }
+ * \defgroup crQUEUE_SEND crQUEUE_SEND + * \ingroup Tasks + */ +#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \ +{ \ + *pxResult = xQueueCRSend( pxQueue, pvItemToQueue, xTicksToWait ); \ + if( *pxResult == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( xHandle ); \ + *pxResult = xQueueCRSend( pxQueue, pvItemToQueue, 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( xHandle ); \ + *pxResult = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_RECEIVE(
+                     xCoRoutineHandle xHandle,
+                     xQueueHandle pxQueue,
+                     void *pvBuffer,
+                     portTickType xTicksToWait,
+                     portBASE_TYPE *pxResult
+                 )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_RECEIVE can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue from which the data will be received. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvBuffer The buffer into which the received item is to be copied. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied into pvBuffer. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for data to become available from the queue, should data not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_RATE_MS can be used to convert ticks to milliseconds (see the + * crQUEUE_SEND example). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully retrieved from the queue, otherwise it will be set to + * an error code as defined within ProjDefs.h. + * + * Example usage: +
+ // A co-routine receives the number of an LED to flash from a queue.  It
+ // blocks on the queue until the number is received.
+ static void prvCoRoutineFlashWorkTask( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portBASE_TYPE xResult;
+ static unsigned portBASE_TYPE uxLEDToFlash;
+
+    // All co-routines must start with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // Wait for data to become available on the queue.
+        crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+        if( xResult == pdPASS )
+        {
+            // We received the LED to flash - flash it!
+            vParTestToggleLED( uxLEDToFlash );
+        }
+    }
+
+    crEND();
+ }
+ * \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \ +{ \ + *pxResult = xQueueCRReceive( pxQueue, pvBuffer, xTicksToWait ); \ + if( *pxResult == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( xHandle ); \ + *pxResult = xQueueCRReceive( pxQueue, pvBuffer, 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( xHandle ); \ + *pxResult = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            xQueueHandle pxQueue,
+                            void *pvItemToQueue,
+                            portBASE_TYPE xCoRoutinePreviouslyWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue + * that is being used from within a co-routine. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto + * the same queue multiple times from a single interrupt. The first call + * should always pass in pdFALSE. Subsequent calls should pass in + * the value returned from the previous call. + * + * @return pdTRUE if a co-routine was woken by posting onto the queue. This is + * used by the ISR to determine if a context switch may be required following + * the ISR. + * + * Example usage: +
+ // A co-routine that blocks on a queue waiting for characters to be received.
+ static void vReceivingCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ portCHAR cRxedChar;
+ portBASE_TYPE xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Wait for data to become available on the queue.  This assumes the
+         // queue xCommsRxQueue has already been created!
+         crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+         // Was a character received?
+         if( xResult == pdPASS )
+         {
+             // Process the character here.
+         }
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to send characters received on a serial port to
+ // a co-routine.
+ void vUART_ISR( void )
+ {
+ portCHAR cRxedChar;
+ portBASE_TYPE xCRWokenByPost = pdFALSE;
+
+     // We loop around reading characters until there are none left in the UART.
+     while( UART_RX_REG_NOT_EMPTY() )
+     {
+         // Obtain the character from the UART.
+         cRxedChar = UART_RX_REG;
+
+         // Post the character onto a queue.  xCRWokenByPost will be pdFALSE
+         // the first time around the loop.  If the post causes a co-routine
+         // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
+         // In this manner we can ensure that if more than one co-routine is
+         // blocked on the queue only one is woken by this ISR no matter how
+         // many characters are posted to the queue.
+         xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
+     }
+ }
+ * \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) + + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            xQueueHandle pxQueue,
+                            void *pvBuffer,
+                            portBASE_TYPE * pxCoRoutineWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data + * from a queue that is being used from within a co-routine (a co-routine + * posted to the queue). + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvBuffer A pointer to a buffer into which the received item will be + * placed. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from the queue into + * pvBuffer. + * + * @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become + * available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a + * co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise + * *pxCoRoutineWoken will remain unchanged. + * + * @return pdTRUE an item was successfully received from the queue, otherwise + * pdFALSE. + * + * Example usage: +
+ // A co-routine that posts a character to a queue then blocks for a fixed
+ // period.  The character is incremented each time.
+ static void vSendingCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // cChar holds its value while this co-routine is blocked and must therefore
+ // be declared static.
+ static portCHAR cCharToTx = 'a';
+ portBASE_TYPE xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Send the next character to the queue.
+         crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
+
+         if( xResult == pdPASS )
+         {
+             // The character was successfully posted to the queue.
+         }
+		 else
+		 {
+			// Could not post the character to the queue.
+		 }
+
+         // Enable the UART Tx interrupt to cause an interrupt in this
+		 // hypothetical UART.  The interrupt will obtain the character
+		 // from the queue and send it.
+		 ENABLE_RX_INTERRUPT();
+
+		 // Increment to the next character then block for a fixed period.
+		 // cCharToTx will maintain its value across the delay as it is
+		 // declared static.
+		 cCharToTx++;
+		 if( cCharToTx > 'x' )
+		 {
+			cCharToTx = 'a';
+		 }
+		 crDELAY( 100 );
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to receive characters to send on a UART.
+ void vUART_ISR( void )
+ {
+ portCHAR cCharToTx;
+ portBASE_TYPE xCRWokenByPost = pdFALSE;
+
+     while( UART_TX_REG_EMPTY() )
+     {
+         // Are there any characters in the queue waiting to be sent?
+		 // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
+		 // is woken by the post - ensuring that only a single co-routine is
+		 // woken no matter how many times we go around this loop.
+         if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
+		 {
+			 SEND_CHARACTER( cCharToTx );
+		 }
+     }
+ }
+ * \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( pxQueue, pvBuffer, pxCoRoutineWoken ) + +/* + * This function is intended for internal use by the co-routine macros only. + * The macro nature of the co-routine implementation requires that the + * prototype appears here. The function should not be used by application + * writers. + * + * Removes the current co-routine from its ready list and places it in the + * appropriate delayed list. + */ +void vCoRoutineAddToDelayedList( portTickType xTicksToDelay, xList *pxEventList ); + +/* + * This function is intended for internal use by the queue implementation only. + * The function should not be used by application writers. + * + * Removes the highest priority co-routine from the event list and places it in + * the pending ready list. + */ +signed portBASE_TYPE xCoRoutineRemoveFromEventList( const xList *pxEventList ); + + +#endif /* CO_ROUTINE_H */ diff --git a/FreeRTOS/include/list.h b/FreeRTOS/include/list.h new file mode 100644 index 0000000..f3702c1 --- /dev/null +++ b/FreeRTOS/include/list.h @@ -0,0 +1,282 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* + * This is the list implementation used by the scheduler. While it is tailored + * heavily for the schedulers needs, it is also available for use by + * application code. + * + * xLists can only store pointers to xListItems. Each xListItem contains a + * numeric value (xItemValue). Most of the time the lists are sorted in + * descending item value order. + * + * Lists are created already containing one list item. The value of this + * item is the maximum possible that can be stored, it is therefore always at + * the end of the list and acts as a marker. The list member pxHead always + * points to this marker - even though it is at the tail of the list. This + * is because the tail contains a wrap back pointer to the true head of + * the list. + * + * In addition to it's value, each list item contains a pointer to the next + * item in the list (pxNext), a pointer to the list it is in (pxContainer) + * and a pointer to back to the object that contains it. These later two + * pointers are included for efficiency of list manipulation. There is + * effectively a two way link between the object containing the list item and + * the list item itself. + * + * + * \page ListIntroduction List Implementation + * \ingroup FreeRTOSIntro + */ + +/* + Changes from V4.3.1 + + + Included local const within listGET_OWNER_OF_NEXT_ENTRY() to assist + compiler with optimisation. Thanks B.R. +*/ + +#ifndef LIST_H +#define LIST_H + +/* + * Definition of the only type of object that a list can contain. + */ +struct xLIST_ITEM +{ + portTickType xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ + volatile struct xLIST_ITEM * pxNext; /*< Pointer to the next xListItem in the list. */ + volatile struct xLIST_ITEM * pxPrevious;/*< Pointer to the previous xListItem in the list. */ + void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ + void * pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ +}; +typedef struct xLIST_ITEM xListItem; /* For some reason lint wants this as two separate definitions. */ + +struct xMINI_LIST_ITEM +{ + portTickType xItemValue; + volatile struct xLIST_ITEM *pxNext; + volatile struct xLIST_ITEM *pxPrevious; +}; +typedef struct xMINI_LIST_ITEM xMiniListItem; + +/* + * Definition of the type of queue used by the scheduler. + */ +typedef struct xLIST +{ + volatile unsigned portBASE_TYPE uxNumberOfItems; + volatile xListItem * pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to pvListGetOwnerOfNextEntry (). */ + volatile xMiniListItem xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ +} xList; + +/* + * Access macro to set the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( pxListItem )->pvOwner = ( void * ) pxOwner + +/* + * Access macro to set the value of the list item. In most cases the value is + * used to sort the list in descending order. + * + * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( pxListItem )->xItemValue = xValue + +/* + * Access macro the retrieve the value of the list item. The value can + * represent anything - for example a the priority of a task, or the time at + * which a task should be unblocked. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) + +/* + * Access macro to determine if a list contains any items. The macro will + * only have the value true if the list is empty. + * + * \page listLIST_IS_EMPTY listLIST_IS_EMPTY + * \ingroup LinkedList + */ +#define listLIST_IS_EMPTY( pxList ) ( ( pxList )->uxNumberOfItems == ( unsigned portBASE_TYPE ) 0 ) + +/* + * Access macro to return the number of items in the list. + */ +#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) + +/* + * Access function to obtain the owner of the next entry in a list. + * + * The list member pxIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list + * and returns that entries pxOwner parameter. Using multiple calls to this + * function it is therefore possible to move through every item contained in + * a list. + * + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the next item owner is to be returned. + * + * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ +{ \ +xList * const pxConstList = pxList; \ + /* Increment the index to the next item and return the item, ensuring */ \ + /* we don't return the marker used at the end of the list. */ \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + if( ( pxConstList )->pxIndex == ( xListItem * ) &( ( pxConstList )->xListEnd ) ) \ + { \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + } \ + pxTCB = ( pxConstList )->pxIndex->pvOwner; \ +} + + +/* + * Access function to obtain the owner of the first entry in a list. Lists + * are normally sorted in ascending item value order. + * + * This function returns the pxOwner member of the first item in the list. + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the owner of the head item is to be + * returned. + * + * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( pxList->uxNumberOfItems != ( unsigned portBASE_TYPE ) 0 ) ? ( (&( pxList->xListEnd ))->pxNext->pvOwner ) : ( NULL ) ) + +/* + * Check to see if a list item is within a list. The list item maintains a + * "container" pointer that points to the list it is in. All this macro does + * is check to see if the container and the list match. + * + * @param pxList The list we want to know if the list item is within. + * @param pxListItem The list item we want to know if is in the list. + * @return pdTRUE is the list item is in the list, otherwise pdFALSE. + * pointer against + */ +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( pxListItem )->pvContainer == ( void * ) pxList ) + +/* + * Must be called before a list is used! This initialises all the members + * of the list structure and inserts the xListEnd item into the list as a + * marker to the back of the list. + * + * @param pxList Pointer to the list being initialised. + * + * \page vListInitialise vListInitialise + * \ingroup LinkedList + */ +void vListInitialise( xList *pxList ); + +/* + * Must be called before a list item is used. This sets the list container to + * null so the item does not think that it is already contained in a list. + * + * @param pxItem Pointer to the list item being initialised. + * + * \page vListInitialiseItem vListInitialiseItem + * \ingroup LinkedList + */ +void vListInitialiseItem( xListItem *pxItem ); + +/* + * Insert a list item into a list. The item will be inserted into the list in + * a position determined by its item value (descending item value order). + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The item to that is to be placed in the list. + * + * \page vListInsert vListInsert + * \ingroup LinkedList + */ +void vListInsert( xList *pxList, xListItem *pxNewListItem ); + +/* + * Insert a list item into a list. The item will be inserted in a position + * such that it will be the last item within the list returned by multiple + * calls to listGET_OWNER_OF_NEXT_ENTRY. + * + * The list member pvIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pvIndex to the next item in the list. + * Placing an item in a list using vListInsertEnd effectively places the item + * in the list position pointed to by pvIndex. This means that every other + * item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before + * the pvIndex parameter again points to the item being inserted. + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The list item to be inserted into the list. + * + * \page vListInsertEnd vListInsertEnd + * \ingroup LinkedList + */ +void vListInsertEnd( xList *pxList, xListItem *pxNewListItem ); + +/* + * Remove an item from a list. The list item has a pointer to the list that + * it is in, so only the list item need be passed into the function. + * + * @param vListRemove The item to be removed. The item will remove itself from + * the list pointed to by it's pxContainer parameter. + * + * \page vListRemove vListRemove + * \ingroup LinkedList + */ +void vListRemove( xListItem *pxItemToRemove ); + + + +#endif + diff --git a/FreeRTOS/include/portable.h b/FreeRTOS/include/portable.h new file mode 100644 index 0000000..bbd5d9a --- /dev/null +++ b/FreeRTOS/include/portable.h @@ -0,0 +1,239 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http:www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http:www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/*----------------------------------------------------------- + * Portable layer API. Each function must be defined for each port. + *----------------------------------------------------------*/ + +#ifndef PORTABLE_H +#define PORTABLE_H + +/* Include the macro file relevant to the port being used. */ + +#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT + #include "..\..\source\portable\owatcom\16bitdos\pc\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT + #include "..\..\source\portable\owatcom\16bitdos\flsh186\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef GCC_MEGA_AVR + #include "../portable/GCC/ATMega323/portmacro.h" +#endif + +#ifdef IAR_MEGA_AVR + #include "../portable/IAR/ATMega323/portmacro.h" +#endif + +#ifdef MPLAB_PIC24_PORT + #include "..\..\Source\portable\MPLAB\PIC24_dsPIC\portmacro.h" +#endif + +#ifdef MPLAB_DSPIC_PORT + #include "..\..\Source\portable\MPLAB\PIC24_dsPIC\portmacro.h" +#endif + +#ifdef MPLAB_PIC18F_PORT + #include "..\..\source\portable\MPLAB\PIC18F\portmacro.h" +#endif + +#ifdef _FEDPICC + #include "libFreeRTOS/Include/portmacro.h" +#endif + +#ifdef SDCC_CYGNAL + #include "../../Source/portable/SDCC/Cygnal/portmacro.h" +#endif + +#ifdef GCC_ARM7 + #include "FreeRTOS/portable/GCC/ARM7_LPC2000/portmacro.h" +#endif + +#ifdef GCC_ARM7_ECLIPSE + #include "portmacro.h" +#endif + +#ifdef ROWLEY_LPC23xx + #include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h" +#endif + +#ifdef GCC_MSP430 + #include "../../Source/portable/GCC/MSP430F449/portmacro.h" +#endif + +#ifdef ROWLEY_MSP430 + #include "../../Source/portable/Rowley/MSP430F449/portmacro.h" +#endif + +#ifdef KEIL_ARM7 + #include "..\..\Source\portable\Keil\ARM7\portmacro.h" +#endif + +#ifdef SAM7_GCC + #include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h" +#endif + +#ifdef SAM7_IAR + #include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h" +#endif + +#ifdef LPC2000_IAR + #include "..\..\Source\portable\IAR\LPC2000\portmacro.h" +#endif + +#ifdef STR71X_IAR + #include "..\..\Source\portable\IAR\STR71x\portmacro.h" +#endif + +#ifdef STR75X_IAR + #include "..\..\Source\portable\IAR\STR75x\portmacro.h" +#endif + +#ifdef STR75X_GCC + #include "..\..\Source\portable\GCC\STR75x\portmacro.h" +#endif + +#ifdef STR91X_IAR + #include "..\..\Source\portable\IAR\STR91x\portmacro.h" +#endif + +#ifdef GCC_H8S + #include "../../Source/portable/GCC/H8S2329/portmacro.h" +#endif + +#ifdef GCC_AT91FR40008 + #include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h" +#endif + +#ifdef RVDS_ARMCM3_LM3S102 + #include "../../Source/portable/RVDS/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3_LM3S102 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARM_CM3 + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARMCM3_LM + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef HCS12_CODE_WARRIOR + #include "../../Source/portable/CodeWarrior/HCS12/portmacro.h" +#endif + +#ifdef MICROBLAZE_GCC + #include "../../Source/portable/GCC/MicroBlaze/portmacro.h" +#endif + +#ifdef TERN_EE + #include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h" +#endif + +#ifdef GCC_HCS12 + #include "../../Source/portable/GCC/HCS12/portmacro.h" +#endif + +#ifdef GCC_MCF5235 + #include "../../Source/portable/GCC/MCF5235/portmacro.h" +#endif + +#ifdef BCC_INDUSTRIAL_PC_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\PC\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef BCC_FLASH_LITE_186_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef __GNUC__ + #ifdef __AVR32_AVR32A__ + #include "portmacro.h" + #endif +#endif + +#ifdef __ICCAVR32__ + #ifdef __CORE__ + #if __CORE__ == __AVR32A__ + #include "portmacro.h" + #endif + #endif +#endif + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ); + +/* + * Map to the memory management routines required for the port. + */ +void vPortUsedMem(int *bytesFree, int *bytesUsed, int *blocksFree); +void *pvPortMalloc( size_t xSize ); +void vPortFree( void *pv ); +void vPortInitialiseBlocks( void ); + +/* + * Setup the hardware ready for the scheduler to take control. This generally + * sets up a tick interrupt and sets timers for the correct tick frequency. + */ +portBASE_TYPE xPortStartScheduler( void ); + +/* + * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so + * the hardware is left in its original condition after the scheduler stops + * executing. + */ +void vPortEndScheduler( void ); + + +#endif /* PORTABLE_H */ + diff --git a/FreeRTOS/include/projdefs.h b/FreeRTOS/include/projdefs.h new file mode 100644 index 0000000..37a6622 --- /dev/null +++ b/FreeRTOS/include/projdefs.h @@ -0,0 +1,59 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +#ifndef PROJDEFS_H +#define PROJDEFS_H + +/* Defines to prototype to which task functions must conform. */ +typedef void (*pdTASK_CODE)( void * ); + +#define pdTRUE ( 1 ) +#define pdFALSE ( 0 ) + +#define pdPASS ( 1 ) +#define pdFAIL ( 0 ) +#define errQUEUE_EMPTY ( 0 ) +#define errQUEUE_FULL ( 0 ) + +/* Error definitions. */ +#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) +#define errNO_TASK_TO_RUN ( -2 ) +#define errQUEUE_BLOCKED ( -4 ) +#define errQUEUE_YIELD ( -5 ) + +#endif /* PROJDEFS_H */ + + + diff --git a/FreeRTOS/include/queue.h b/FreeRTOS/include/queue.h new file mode 100644 index 0000000..4a6c9a6 --- /dev/null +++ b/FreeRTOS/include/queue.h @@ -0,0 +1,474 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +#ifndef QUEUE_H +#define QUEUE_H + +typedef void * xQueueHandle; + +/** + * queue. h + *
+ xQueueHandle xQueueCreate( 
+                              unsigned portBASE_TYPE uxQueueLength, 
+                              unsigned portBASE_TYPE uxItemSize 
+                          );
+ * 
+ * + * Creates a new queue instance. This allocates the storage required by the + * new queue and returns a handle for the queue. + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then 0 is + * returned. + * + * Example usage: +
+ struct AMessage
+ {
+    portCHAR ucMessageID;
+    portCHAR ucData[ 20 ];
+ };
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+
+    // Create a queue capable of containing 10 unsigned long values.
+    xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
+    if( xQueue1 == 0 )
+    {
+        // Queue was not created and must not be used.
+    }
+
+    // Create a queue capable of containing 10 pointers to AMessage structures.
+    // These should be passed by pointer as they contain a lot of data.
+    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+    if( xQueue2 == 0 )
+    {
+        // Queue was not created and must not be used.
+    }
+
+    // ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueSend( 
+                             xQueueHandle xQueue, 
+                             const void * pvItemToQueue, 
+                             portTickType xTicksToWait 
+                         );
+ * 
+ * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0. The + * time is defined in tick periods so the constant portTICK_RATE_MS + * should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+    portCHAR ucMessageID;
+    portCHAR ucData[ 20 ];
+ } xMessage;
+
+ unsigned portLONG ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+    // Create a queue capable of containing 10 unsigned long values.
+    xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
+
+    // Create a queue capable of containing 10 pointers to AMessage structures.
+    // These should be passed by pointer as they contain a lot of data.
+    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+    // ...
+
+    if( xQueue1 != 0 )
+    {
+        // Send an unsigned long.  Wait for 10 ticks for space to become 
+        // available if necessary.
+        if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+        {
+            // Failed to post the message, even after 10 ticks.
+        }
+    }
+
+    if( xQueue2 != 0 )
+    {
+        // Send a pointer to a struct AMessage object.  Don't block if the
+        // queue is already full.
+        pxMessage = & xMessage;
+        xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+    }
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueSend( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueReceive( 
+                                xQueueHandle xQueue, 
+                                void *pvBuffer, 
+                                portTickType xTicksToWait 
+                            );
+ * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+    portCHAR ucMessageID;
+    portCHAR ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+ 
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+    // Create a queue capable of containing 10 pointers to AMessage structures.
+    // These should be passed by pointer as they contain a lot of data.
+    xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+    if( xQueue == 0 )
+    {
+        // Failed to create the queue.
+    }
+
+    // ...
+
+    // Send a pointer to a struct AMessage object.  Don't block if the
+    // queue is already full.
+    pxMessage = & xMessage;
+    xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+    if( xQueue != 0 )
+    {
+        // Receive a message on the created queue.  Block for 10 ticks if a
+        // message is not immediately available.
+        if( xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+        {
+            // pcRxedMessage now points to the struct AMessage variable posted
+            // by vATask.
+        }
+    }
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueReceive( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait ); + +/** + * queue. h + *
unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue );
+ * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \page uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue ); + +/** + * queue. h + *
void vQueueDelete( xQueueHandle xQueue );
+ * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \page vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( xQueueHandle xQueue ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendFromISR( 
+                                    xQueueHandle pxQueue, 
+                                    const void *pvItemToQueue, 
+                                    portBASE_TYPE xTaskPreviouslyWoken 
+                                );
+ 
+ * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param cTaskPreviouslyWoken This is included so an ISR can post onto + * the same queue multiple times from a single interrupt. The first call + * should always pass in pdFALSE. Subsequent calls should pass in + * the value returned from the previous call. See the file serial .c in the + * PC port for a good example of this mechanism. + * + * @return pdTRUE if a task was woken by posting onto the queue. This is + * used by the ISR to determine if a context switch may be required following + * the ISR. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ portCHAR cIn;
+ portBASE_TYPE xTaskWokenByPost;
+
+    // We have not woken a task at the start of the ISR.
+    cTaskWokenByPost = pdFALSE;
+
+    // Loop until the buffer is empty.
+    do
+    {
+        // Obtain a byte from the buffer.
+        cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );						
+
+        // Post the byte.  The first time round the loop cTaskWokenByPost
+        // will be pdFALSE.  If the queue send causes a task to wake we do
+        // not want the task to run until we have finished the ISR, so
+        // xQueueSendFromISR does not cause a context switch.  Also we 
+        // don't want subsequent posts to wake any other tasks, so we store
+        // the return value back into cTaskWokenByPost so xQueueSendFromISR
+        // knows not to wake any task the next iteration of the loop.
+        xTaskWokenByPost = xQueueSendFromISR( xRxQueue, &cIn, cTaskWokenByPost );
+
+    } while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+    // Now the buffer is empty we can switch context if necessary.
+    if( cTaskWokenByPost )
+    {
+        taskYIELD ();
+    }
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueReceiveFromISR( 
+                                       xQueueHandle pxQueue, 
+                                       void *pvBuffer, 
+                                       portBASE_TYPE *pxTaskWoken 
+                                   ); 
+ * 
+ * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxTaskWoken A task may be blocked waiting for space to become + * available on the queue. If xQueueReceiveFromISR causes such a task to + * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ 
+ xQueueHandle xQueue;
+ 
+ // Function to create a queue and post some values.
+ void vAFunction( void *pvParameters )
+ {
+ portCHAR cValueToPost;
+ const portTickType xBlockTime = ( portTickType )0xff;
+
+    // Create a queue capable of containing 10 characters.
+    xQueue = xQueueCreate( 10, sizeof( portCHAR ) );
+    if( xQueue == 0 )
+    {
+        // Failed to create the queue.
+    }
+
+    // ...
+
+    // Post some characters that will be used within an ISR.  If the queue
+    // is full then this task will block for xBlockTime ticks.
+    cValueToPost = 'a';
+    xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+    cValueToPost = 'b';
+    xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+
+    // ... keep posting characters ... this task may block when the queue
+    // becomes full.
+
+    cValueToPost = 'c';
+    xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+ }
+
+ // ISR that outputs all the characters received on the queue. 
+ void vISR_Routine( void )
+ {
+ portBASE_TYPE xTaskWokenByReceive = pdFALSE;
+ portCHAR cRxedChar;
+
+    while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+    {
+        // A character was received.  Output the character now.
+        vOutputCharacter( cRxedChar );
+
+        // If removing the character from the queue woke the task that was 
+        // posting onto the queue cTaskWokenByReceive will have been set to
+        // pdTRUE.  No matter how many times this loop iterates only one
+        // task will be woken.
+    }
+
+    if( cTaskWokenByPost != ( portCHAR ) pdFALSE;
+    {
+        taskYIELD ();
+    }
+ }
+ 
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ); + + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-rtoutines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ +signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ); +signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ); +signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ); +signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ); + +#endif + diff --git a/FreeRTOS/include/semphr.h b/FreeRTOS/include/semphr.h new file mode 100644 index 0000000..bae09c7 --- /dev/null +++ b/FreeRTOS/include/semphr.h @@ -0,0 +1,292 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +#include "queue.h" + +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +typedef xQueueHandle xSemaphoreHandle; + +#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( unsigned portCHAR ) 1 ) +#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( unsigned portCHAR ) 0 ) +#define semGIVE_BLOCK_TIME ( ( portTickType ) 0 ) + + +/** + * semphr. h + *
vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )
+ * + * Macro that implements a semaphore by using the existing queue mechanism. + * The queue length is 1 as this is a binary semaphore. The data size is 0 + * as we don't want to actually store any data - we just want to know if the + * queue is empty or full. + * + * @param xSemaphore Handle to the created semaphore. Should be of type xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
+    // This is a macro so pass the variable in directly.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * \ingroup Semaphores + */ +#define vSemaphoreCreateBinary( xSemaphore ) { \ + xSemaphore = xQueueCreate( ( unsigned portCHAR ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH ); \ + if( xSemaphore != NULL ) \ + { \ + xSemaphoreGive( xSemaphore ); \ + } \ + } + +/** + * semphr. h + * xSemaphoreTake( + * xSemaphoreHandle xSemaphore, + * portTickType xBlockTime + * ) + * + * Macro to obtain a semaphore. The semaphore must of been created using + * vSemaphoreCreateBinary (). + * + * @param xSemaphore A handle to the semaphore being obtained. This is the + * handle returned by vSemaphoreCreateBinary (); + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_RATE_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. + * + * @return pdTRUE if the semaphore was obtained. pdFALSE if xBlockTime + * expired without the semaphore becoming available. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore = NULL;
+
+ // A task that creates a semaphore.
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+ }
+
+ // A task that uses the semaphore.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xSemaphore != NULL )
+    {
+        // See if we can obtain the semaphore.  If the semaphore is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTake( xSemaphore, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the semaphore and can now access the
+            // shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource.  Release the 
+            // semaphore.
+            xSemaphoreGive( xSemaphore );
+        }
+        else
+        {
+            // We could not obtain the semaphore and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTake xSemaphoreTake + * \ingroup Semaphores + */ +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueReceive( ( xQueueHandle ) xSemaphore, NULL, xBlockTime ) + +/** + * semphr. h + *
xSemaphoreGive( xSemaphoreHandle xSemaphore )
+ * + * Macro to release a semaphore. The semaphore must of been created using + * vSemaphoreCreateBinary (), and obtained using sSemaphoreTake (). + * + * This must not be used from an ISR. See xSemaphoreGiveFromISR () for + * an alternative which can be used from an ISR. + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned by vSemaphoreCreateBinary (); + * + * @return pdTRUE if the semaphore was released. pdFALSE if an error occurred. + * Semaphores are implemented using queues. An error can occur if there is + * no space on the queue to post a message - indicating that the + * semaphore was not first obtained correctly. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore = NULL;
+
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+        {
+            // We would expect this call to fail because we cannot give
+            // a semaphore without first "taking" it!
+        }
+
+        // Obtain the semaphore - don't block if the semaphore is not
+        // immediately available.
+        if( xSemaphoreTake( xSemaphore, ( portTickType ) 0 ) )
+        {
+            // We now have the semaphore and can access the shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource so can free the
+            // semaphore.
+            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+            {
+                // We would not expect this call to fail because we must have
+                // obtained the semaphore to get here.
+            }
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGive xSemaphoreGive + * \ingroup Semaphores + */ +#define xSemaphoreGive( xSemaphore ) xQueueSend( ( xQueueHandle ) xSemaphore, NULL, semGIVE_BLOCK_TIME ) + +/** + * semphr. h + *
+ xSemaphoreGiveFromISR( 
+                          xSemaphoreHandle xSemaphore, 
+                          portSHORT sTaskPreviouslyWoken 
+                      )
+ * + * Macro to release a semaphore. The semaphore must of been created using + * vSemaphoreCreateBinary (), and obtained using xSemaphoreTake (). + * + * This macro can be used from an ISR. + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned by vSemaphoreCreateBinary (); + * + * @param sTaskPreviouslyWoken This is included so an ISR can make multiple calls + * to xSemaphoreGiveFromISR () from a single interrupt. The first call + * should always pass in pdFALSE. Subsequent calls should pass in + * the value returned from the previous call. See the file serial .c in the + * PC port for a good example of using xSemaphoreGiveFromISR (). + * + * @return pdTRUE if a task was woken by releasing the semaphore. This is + * used by the ISR to determine if a context switch may be required following + * the ISR. + * + * Example usage: +
+ #define LONG_TIME 0xffff
+ #define TICKS_TO_WAIT	10
+ xSemaphoreHandle xSemaphore = NULL;
+
+ // Repetitive task.
+ void vATask( void * pvParameters )
+ {
+    for( ;; )
+    {
+        // We want this task to run every 10 ticks or a timer.  The semaphore 
+        // was created before this task was started
+
+        // Block waiting for the semaphore to become available.
+        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
+        {
+            // It is time to execute.
+
+            // ...
+
+            // We have finished our task.  Return to the top of the loop where
+            // we will block on the semaphore until it is time to execute 
+            // again.
+        }
+    }
+ }
+
+ // Timer ISR
+ void vTimerISR( void * pvParameters )
+ {
+ static unsigned portCHAR ucLocalTickCount = 0;
+
+    // A timer tick has occurred.
+
+    // ... Do other time functions.
+
+    // Is it time for vATask () to run?
+    ucLocalTickCount++;
+    if( ucLocalTickCount >= TICKS_TO_WAIT )
+    {
+        // Unblock the task by releasing the semaphore.
+        xSemaphoreGive( xSemaphore );
+
+        // Reset the count so we release the semaphore again in 10 ticks time.
+        ucLocalTickCount = 0;
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * \ingroup Semaphores + */ +#define xSemaphoreGiveFromISR( xSemaphore, xTaskPreviouslyWoken ) xQueueSendFromISR( ( xQueueHandle ) xSemaphore, NULL, xTaskPreviouslyWoken ) + + +#endif + diff --git a/FreeRTOS/include/task.h b/FreeRTOS/include/task.h new file mode 100644 index 0000000..5103f3c --- /dev/null +++ b/FreeRTOS/include/task.h @@ -0,0 +1,970 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* +Changes since V4.3.1: + + + Added xTaskGetSchedulerState() function. +*/ + +#ifndef TASK_H +#define TASK_H + +#include "portable.h" +#include "list.h" + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +#define tskKERNEL_VERSION_NUMBER "V4.4.0" + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an xTaskHandle variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \page xTaskHandle xTaskHandle + * \ingroup Tasks + */ +typedef void * xTaskHandle; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + portBASE_TYPE xOverflowCount; + portTickType xTimeOnEntering; +} xTimeOutType; + +/* + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( unsigned portBASE_TYPE ) 0 ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \page taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \page taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \page taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() + +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \page taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \page taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). */ +#define taskSCHEDULER_NOT_STARTED 0 +#define taskSCHEDULER_RUNNING 1 +#define taskSCHEDULER_SUSPENDED 2 + +/*----------------------------------------------------------- + * TASK CREATION API + *----------------------------------------------------------*/ + +/** + * task. h + *
+ portBASE_TYPE xTaskCreate(
+                              pdTASK_CODE pvTaskCode,
+                              const portCHAR * const pcName,
+                              unsigned portSHORT usStackDepth,
+                              void *pvParameters,
+                              unsigned portBASE_TYPE uxPriority,
+                              xTaskHandle *pvCreatedTask
+                          );
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by tskMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file errors. h + * + * Example usage: +
+ // Task to be created.
+ void vTaskCode( void * pvParameters )
+ {
+     for( ;; )
+     {
+         // Task code goes here.
+     }
+ }
+
+ // Function that creates a task.
+ void vOtherFunction( void )
+ {
+ unsigned char ucParameterToPass;
+ xTaskHandle xHandle;
+		
+     // Create the task, storing the handle.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
+		
+     // Use the handle to delete the task.
+     vTaskDelete( xHandle );
+ }
+   
+ * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +signed portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pvCreatedTask ); + +/** + * task. h + *
void vTaskDelete( xTaskHandle pxTask );
+ * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernels management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param pxTask The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: +
+ void vOtherFunction( void )
+ {
+ xTaskHandle xHandle;
+		
+     // Create the task, storing the handle.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+		
+     // Use the handle to delete the task.
+     vTaskDelete( xHandle );
+ }
+   
+ * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( xTaskHandle pxTask ); + + +/*----------------------------------------------------------- + * TASK CONTROL API + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskDelay( portTickType xTicksToDelay );
+ * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_RATE_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: +
+ // Wait 10 ticks before performing an action.
+ // NOTE:
+ // This is for demonstration only and would be better achieved
+ // using vTaskDelayUntil ().
+ void vTaskFunction( void * pvParameters )
+ {
+ portTickType xDelay, xNextTime;
+
+     // Calc the time at which we want to perform the action
+     // next.
+     xNextTime = xTaskGetTickCount () + ( portTickType ) 10;
+
+     for( ;; )
+     {
+         xDelay = xNextTime - xTaskGetTickCount ();
+         xNextTime += ( portTickType ) 10;
+
+         // Guard against overflow
+         if( xDelay <= ( portTickType ) 10 )
+         {
+             vTaskDelay( xDelay );
+         }
+
+         // Perform action here.
+     }
+ }
+   
+ * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( portTickType xTicksToDelay ); + +/** + * task. h + *
void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement );
+ * + * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by cyclical + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The constant portTICK_RATE_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within vTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * Example usage: +
+ // Perform an action every 10 ticks.
+ void vTaskFunction( void * pvParameters )
+ {
+ portTickType xLastWakeTime;
+ const portTickType xFrequency = 10;
+
+     // Initialise the xLastWakeTime variable with the current time.
+     xLastWakeTime = xTaskGetTickCount ();
+     for( ;; )
+     {
+         // Wait for the next cycle.
+         vTaskDelayUntil( &xLastWakeTime, xFrequency );
+
+         // Perform action here.
+     }
+ }
+   
+ * \defgroup vTaskDelayUntil vTaskDelayUntil + * \ingroup TaskCtrl + */ +void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement ); + +/** + * task. h + *
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );
+ * + * INCLUDE_xTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param pxTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of pxTask. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+		
+     // Create a task, storing the handle.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+		
+     // ...
+
+     // Use the handle to obtain the priority of the created task.
+     // It was created with tskIDLE_PRIORITY, but may have changed
+     // it itself.
+     if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
+     {
+         // The task has changed it's priority.
+     }
+
+     // ...
+
+     // Is our priority higher than the created task?
+     if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
+     {
+         // Our priority (obtained using NULL handle) is higher.
+     }
+ }
+   
+ * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ); + +/** + * task. h + *
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
+ * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param pxTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+		
+     // Create a task, storing the handle.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+     // ...
+
+     // Use the handle to raise the priority of the created task.
+     vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
+
+     // ...
+
+     // Use a NULL handle to raise our priority to the same value.
+     vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
+ }
+   
+ * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ); + +/** + * task. h + *
void vTaskSuspend( xTaskHandle pxTaskToSuspend );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param pxTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+		
+     // Create a task, storing the handle.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+		
+     // ...
+
+     // Use the handle to suspend the created task.
+     vTaskSuspend( xHandle );
+
+     // ...
+		
+     // The created task will not run during this period, unless
+     // another task calls vTaskResume( xHandle ).
+		
+     //...
+		
+
+     // Suspend ourselves.
+     vTaskSuspend( NULL );
+
+     // We cannot get here unless another task calls vTaskResume
+     // with our handle as the parameter.
+ }
+   
+ * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( xTaskHandle pxTaskToSuspend ); + +/** + * task. h + *
void vTaskResume( xTaskHandle pxTaskToResume );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one of more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param pxTaskToResume Handle to the task being readied. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+		
+     // Create a task, storing the handle.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+		
+     // ...
+
+     // Use the handle to suspend the created task.
+     vTaskSuspend( xHandle );
+
+     // ...
+	
+     // The created task will not run during this period, unless
+     // another task calls vTaskResume( xHandle ).
+		
+     //...
+		
+
+     // Resume the suspended task ourselves.
+     vTaskResume( xHandle );
+
+     // The created task will once again get microcontroller processing
+     // time in accordance with it priority within the system.
+ }
+   
+ * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( xTaskHandle pxTaskToResume ); + +/** + * task. h + *
void xTaskResumeFromISR( xTaskHandle pxTaskToResume );
+ * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one of more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * @param pxTaskToResume Handle to the task being readied. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume ); + +/*----------------------------------------------------------- + * SCHEDULER CONTROL + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskStartScheduler( void );
+ * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. This function + * does not return until an executing task calls vTaskEndScheduler (). + * + * At least one task should be created via a call to xTaskCreate () + * before calling vTaskStartScheduler (). The idle task is created + * automatically when the first application task is created. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: +
+ void vAFunction( void )
+ {
+     // Create at least one task before starting the kernel.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+     // Start the real time kernel with preemption.
+     vTaskStartScheduler ();
+
+     // Will not get here unless a task calls vTaskEndScheduler ()
+ }
+   
+ * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ); + +/** + * task. h + *
void vTaskEndScheduler( void );
+ * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: +
+ void vTaskCode( void * pvParameters )
+ {
+     for( ;; )
+     {
+         // Task code goes here.
+
+         // At some point we want to end the real time kernel processing
+         // so call ...
+         vTaskEndScheduler ();
+     }
+ }
+
+ void vAFunction( void )
+ {
+     // Create at least one task before starting the kernel.
+     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+     // Start the real time kernel with preemption.
+     vTaskStartScheduler ();
+
+     // Will only get here when the vTaskCode () task has called
+     // vTaskEndScheduler ().  When we get here we are back to single task
+     // execution.
+ }
+   
+ * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ); + +/** + * task. h + *
void vTaskSuspendAll( void );
+ * + * Suspends all real time kernel activity while keeping interrupts (including the + * kernel tick) enabled. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+     for( ;; )
+     {
+         // Task code goes here.
+
+         // ...
+
+         // At some point the task wants to perform a long operation during
+         // which it does not want to get swapped out.  It cannot use
+         // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+         // operation may cause interrupts to be missed - including the
+         // ticks.
+
+         // Prevent the real time kernel swapping out the task.
+         vTaskSuspendAll ();
+
+         // Perform the operation here.  There is no need to use critical
+         // sections as we have all the microcontroller processing time.
+         // During this time interrupts will still operate and the kernel
+         // tick count will be maintained.
+
+         // ...
+
+         // The operation is complete.  Restart the kernel.
+         xTaskResumeAll ();
+     }
+ }
+   
+ * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ); + +/** + * task. h + *
portCHAR xTaskResumeAll( void );
+ * + * Resumes real time kernel activity following a call to vTaskSuspendAll (). + * After a call to vTaskSuspendAll () the kernel will take control of which + * task is executing at any time. + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+     for( ;; )
+     {
+         // Task code goes here.
+
+         // ...
+
+         // At some point the task wants to perform a long operation during
+         // which it does not want to get swapped out.  It cannot use
+         // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+         // operation may cause interrupts to be missed - including the
+         // ticks.
+
+         // Prevent the real time kernel swapping out the task.
+         vTaskSuspendAll ();
+
+         // Perform the operation here.  There is no need to use critical
+         // sections as we have all the microcontroller processing time.
+         // During this time interrupts will still operate and the real
+         // time kernel tick count will be maintained.
+
+         // ...
+
+         // The operation is complete.  Restart the kernel.  We want to force
+         // a context switch - but there is no point if resuming the scheduler
+         // caused a context switch already.
+         if( !xTaskResumeAll () )
+         {
+              taskYIELD ();
+         }
+     }
+ }
+   
+ * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +signed portBASE_TYPE xTaskResumeAll( void ); + + +/*----------------------------------------------------------- + * TASK UTILITIES + *----------------------------------------------------------*/ + +/** + * task. h + *
volatile portTickType xTaskGetTickCount( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \page xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +portTickType xTaskGetTickCount( void ); + +/** + * task. h + *
unsigned portSHORT uxTaskGetNumberOfTasks( void );
+ * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \page uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ); + +/** + * task. h + *
void vTaskList( portCHAR *pcWriteBuffer );
+ * + * configUSE_TRACE_FACILITY, INCLUDE_vTaskDelete and INCLUDE_vTaskSuspend + * must all be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * NOTE: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ascii form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \page vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( signed portCHAR *pcWriteBuffer ); + +/** + * task. h + *
void vTaskStartTrace( portCHAR * pcBuffer, unsigned portBASE_TYPE uxBufferSize );
+ * + * Starts a real time kernel activity trace. The trace logs the identity of + * which task is running when. + * + * The trace file is stored in binary format. A separate DOS utility called + * convtrce.exe is used to convert this into a tab delimited text file which + * can be viewed and plotted in a spread sheet. + * + * @param pcBuffer The buffer into which the trace will be written. + * + * @param ulBufferSize The size of pcBuffer in bytes. The trace will continue + * until either the buffer in full, or ulTaskEndTrace () is called. + * + * \page vTaskStartTrace vTaskStartTrace + * \ingroup TaskUtils + */ +void vTaskStartTrace( signed portCHAR * pcBuffer, unsigned portLONG ulBufferSize ); + +/** + * task. h + *
unsigned portLONG ulTaskEndTrace( void );
+ * + * Stops a kernel activity trace. See vTaskStartTrace (). + * + * @return The number of bytes that have been written into the trace buffer. + * + * \page usTaskEndTrace usTaskEndTrace + * \ingroup TaskUtils + */ +unsigned portLONG ulTaskEndTrace( void ); + + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + *----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. + */ +inline void vTaskIncrementTick( void ); + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks,the constant + * portTICK_RATE_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( xList *pxEventList, portTickType xTicksToWait ); + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList () will be called if either an event occurs to + * unblock a task, or the block timeout period expires. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +signed portBASE_TYPE xTaskRemoveFromEventList( const xList *pxEventList ); + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * INCLUDE_vTaskCleanUpResources and INCLUDE_vTaskSuspend must be defined as 1 + * for this function to be available. + * See the configuration section for more information. + * + * Empties the ready and delayed queues of task control blocks, freeing the + * memory allocated for the task control block and task stacks as it goes. + */ +void vTaskCleanUpResources( void ); + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +inline void vTaskSwitchContext( void ); + +/* + * Return the handle of the calling task. + */ +xTaskHandle xTaskGetCurrentTaskHandle( void ); + +/* + * Capture the current time status for future reference. + */ +void vTaskSetTimeOutState( xTimeOutType *pxTimeOut ); + +/* + * Compare the time status now with that previously captured to see if the + * timeout has expired. + */ +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType *pxTimeOut, portTickType * const pxTicksToWait ); + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ); + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +portBASE_TYPE xTaskGetSchedulerState( void ); + +#endif /* TASK_H */ + + + diff --git a/FreeRTOS/list.c b/FreeRTOS/list.c new file mode 100644 index 0000000..3faa8b4 --- /dev/null +++ b/FreeRTOS/list.c @@ -0,0 +1,204 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* +Changes from V1.2.0 + + + Removed the volatile modifier from the function parameters. This was + only ever included to prevent compiler warnings. Now warnings are + removed by casting parameters where the calls are made. + + + prvListGetOwnerOfNextEntry() and prvListGetOwnerOfHeadEntry() have been + removed from the c file and added as macros to the h file. + + + uxNumberOfItems has been added to the list structure. This removes the + need for a pointer comparison when checking if a list is empty, and so + is slightly faster. + + + Removed the NULL check in vListRemove(). This makes the call faster but + necessitates any application code utilising the list implementation to + ensure NULL pointers are not passed. + +Changes from V2.0.0 + + + Double linked the lists to allow faster removal item removal. + +Changes from V2.6.1 + + + Make use of the new portBASE_TYPE definition where ever appropriate. + +Changes from V3.0.0 + + + API changes as described on the FreeRTOS.org WEB site. + +Changes from V3.2.4 + + + Removed the pxHead member of the xList structure. This always pointed + to the same place so has been removed to free a few bytes of RAM. + + + Introduced the xMiniListItem structure that does not include the + xListItem members that are not required by the xListEnd member of a list. + Again this was done to reduce RAM usage. + + + Changed the volatile definitions of some structure members to clean up + the code where the list structures are used. + +Changes from V4.0.4 + + + Optimised vListInsert() in the case when the wake time is the maximum + tick count value. +*/ + +#include +#include "FreeRTOS.h" +#include "list.h" + +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +void vListInitialise( xList *pxList ) +{ + /* The list structure contains a list item which is used to mark the + end of the list. To initialise the list the list end is inserted + as the only list entry. */ + pxList->pxIndex = ( xListItem * ) &( pxList->xListEnd ); + + /* The list end value is the highest possible value in the list to + ensure it remains at the end of the list. */ + pxList->xListEnd.xItemValue = portMAX_DELAY; + + /* The list end next and previous pointers point to itself so we know + when the list is empty. */ + pxList->xListEnd.pxNext = ( xListItem * ) &( pxList->xListEnd ); + pxList->xListEnd.pxPrevious = ( xListItem * ) &( pxList->xListEnd ); + + pxList->uxNumberOfItems = 0; +} +/*-----------------------------------------------------------*/ + +void vListInitialiseItem( xListItem *pxItem ) +{ + /* Make sure the list item is not recorded as being on a list. */ + pxItem->pvContainer = NULL; +} +/*-----------------------------------------------------------*/ + +void vListInsertEnd( xList *pxList, xListItem *pxNewListItem ) +{ +volatile xListItem * pxIndex; + + /* Insert a new list item into pxList, but rather than sort the list, + makes the new list item the last item to be removed by a call to + pvListGetOwnerOfNextEntry. This means it has to be the item pointed to by + the pxIndex member. */ + pxIndex = pxList->pxIndex; + + pxNewListItem->pxNext = pxIndex->pxNext; + pxNewListItem->pxPrevious = pxList->pxIndex; + pxIndex->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem; + pxIndex->pxNext = ( volatile xListItem * ) pxNewListItem; + pxList->pxIndex = ( volatile xListItem * ) pxNewListItem; + + /* Remember which list the item is in. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListInsert( xList *pxList, xListItem *pxNewListItem ) +{ +volatile xListItem *pxIterator; +portTickType xValueOfInsertion; + + /* Insert the new list item into the list, sorted in ulListItem order. */ + xValueOfInsertion = pxNewListItem->xItemValue; + + /* If the list already contains a list item with the same item value then + the new list item should be placed after it. This ensures that TCB's which + are stored in ready lists (all of which have the same ulListItem value) + get an equal share of the CPU. However, if the xItemValue is the same as + the back marker the iteration loop below will not end. This means we need + to guard against this by checking the value first and modifying the + algorithm slightly if necessary. */ + if( xValueOfInsertion == portMAX_DELAY ) + { + pxIterator = pxList->xListEnd.pxPrevious; + } + else + { + for( pxIterator = ( xListItem * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) + { + /* There is nothing to do here, we are just iterating to the + wanted insertion position. */ + } + } + + pxNewListItem->pxNext = pxIterator->pxNext; + pxNewListItem->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem; + pxNewListItem->pxPrevious = pxIterator; + pxIterator->pxNext = ( volatile xListItem * ) pxNewListItem; + + /* Remember which list the item is in. This allows fast removal of the + item later. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListRemove( xListItem *pxItemToRemove ) +{ +xList * pxList; + + pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; + pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; + + /* The list item knows which list it is in. Obtain the list from the list + item. */ + pxList = ( xList * ) pxItemToRemove->pvContainer; + + /* Make sure the index is left pointing to a valid item. */ + if( pxList->pxIndex == pxItemToRemove ) + { + pxList->pxIndex = pxItemToRemove->pxPrevious; + } + + pxItemToRemove->pvContainer = NULL; + ( pxList->uxNumberOfItems )--; +} +/*-----------------------------------------------------------*/ + diff --git a/FreeRTOS/portable/GCC/ARM7_LPC2000/Makefile b/FreeRTOS/portable/GCC/ARM7_LPC2000/Makefile new file mode 100644 index 0000000..c8d4a89 --- /dev/null +++ b/FreeRTOS/portable/GCC/ARM7_LPC2000/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=port.c portISR.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/FreeRTOS/portable/GCC/ARM7_LPC2000/port.c b/FreeRTOS/portable/GCC/ARM7_LPC2000/port.c new file mode 100644 index 0000000..ce74e71 --- /dev/null +++ b/FreeRTOS/portable/GCC/ARM7_LPC2000/port.c @@ -0,0 +1,245 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM7 port. + * + * Components that can be compiled to either ARM or THUMB mode are + * contained in this file. The ISR routines, which can only be compiled + * to ARM mode are contained in portISR.c. + *----------------------------------------------------------*/ + +/* + Changes from V2.5.2 + + + ulCriticalNesting is now saved as part of the task context, as is + therefore added to the initial task stack during pxPortInitialiseStack. + + Changes from V3.2.2 + + + Bug fix - The prescale value for the timer setup is now written to T0_PR + instead of T0_PC. This bug would have had no effect unless a prescale + value was actually used. +*/ + + +/* Standard includes. */ +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Constants required to setup the task context. */ +#define portINITIAL_SPSR ( ( portSTACK_TYPE ) 0x1f ) /* System mode, ARM mode, interrupts enabled. */ +#define portTHUMB_MODE_BIT ( ( portSTACK_TYPE ) 0x20 ) +#define portINSTRUCTION_SIZE ( ( portSTACK_TYPE ) 4 ) +#define portNO_CRITICAL_SECTION_NESTING ( ( portSTACK_TYPE ) 0 ) + +/* Constants required to setup the tick ISR. */ +#define portENABLE_TIMER ( ( unsigned portCHAR ) 0x01 ) +#define portPRESCALE_VALUE 0x00 +#define portINTERRUPT_ON_MATCH ( ( unsigned portLONG ) 0x01 ) +#define portRESET_COUNT_ON_MATCH ( ( unsigned portLONG ) 0x02 ) + +/*-----------------------------------------------------------*/ + +/* Setup the timer to generate the tick interrupts. */ +static void prvSetupTimerInterrupt( void ); + +/* + * The scheduler can only be started from ARM mode, so + * vPortISRStartFirstSTask() is defined in portISR.c. + */ +extern void vPortISRStartFirstTask( void ); + +/*-----------------------------------------------------------*/ + +/* + * Initialise the stack of a task to look exactly as if a call to + * portSAVE_CONTEXT had been called. + * + * See header file for description. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) +{ +portSTACK_TYPE *pxOriginalTOS; + + pxOriginalTOS = pxTopOfStack; + + /* Setup the initial stack of the task. The stack is set exactly as + expected by the portRESTORE_CONTEXT() macro. */ + + /* First on the stack is the return address - which in this case is the + start of the task. The offset is added to make the return address appear + as it would within an IRQ ISR. */ + *pxTopOfStack = ( portSTACK_TYPE ) pxCode + portINSTRUCTION_SIZE; + pxTopOfStack--; + + *pxTopOfStack = ( portSTACK_TYPE ) 0xaaaaaaaa; /* R14 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) pxOriginalTOS; /* Stack used when task starts goes in R13. */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x12121212; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x11111111; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x10101010; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x09090909; /* R9 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x08080808; /* R8 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x07070707; /* R7 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x06060606; /* R6 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x05050505; /* R5 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x04040404; /* R4 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x03030303; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x02020202; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = ( portSTACK_TYPE ) 0x01010101; /* R1 */ + pxTopOfStack--; + + /* When the task starts is will expect to find the function parameter in + R0. */ + *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */ + pxTopOfStack--; + + /* The last thing onto the stack is the status register, which is set for + system mode, with interrupts enabled. */ + *pxTopOfStack = ( portSTACK_TYPE ) portINITIAL_SPSR; + + #ifdef THUMB_INTERWORK + { + /* We want the task to start in thumb mode. */ + *pxTopOfStack |= portTHUMB_MODE_BIT; + } + #endif + + pxTopOfStack--; + + /* Some optimisation levels use the stack differently to others. This + means the interrupt flags cannot always be stored on the stack and will + instead be stored in a variable, which is then saved as part of the + tasks context. */ + *pxTopOfStack = portNO_CRITICAL_SECTION_NESTING; + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xPortStartScheduler( void ) +{ + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); + + /* Start the first task. */ + vPortISRStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* It is unlikely that the ARM port will require this function as there + is nothing to return to. */ +} +/*-----------------------------------------------------------*/ + +/* + * Setup the timer 0 to generate the tick interrupts at the required frequency. + */ +static void prvSetupTimerInterrupt( void ) +{ +unsigned portLONG ulCompareMatch; + + SCB_PCONP |= SCB_PCONP_PCTIM0; + + /* A 1ms tick does not require the use of the timer prescale. This is + defaulted to zero but can be used if necessary. */ + T0_PR = portPRESCALE_VALUE; + + /* Calculate the match value required for our wanted tick rate. */ + ulCompareMatch = configCPU_CLOCK_HZ / configTICK_RATE_HZ; + + /* Protect against divide by zero. Using an if() statement still results + in a warning - hence the #if. */ + #if portPRESCALE_VALUE != 0 + { + ulCompareMatch /= ( portPRESCALE_VALUE + 1 ); + } + #endif + T0_MR0 = ulCompareMatch; + + /* Generate tick with timer 0 compare match. */ + T0_MCR = portRESET_COUNT_ON_MATCH | portINTERRUPT_ON_MATCH; + + /* Setup the VIC for the timer. */ + VIC_IntSelect &= ~VIC_IntSelect_Timer0; + VIC_IntEnable = VIC_IntEnable_Timer0; + + /* The ISR installed depends on whether the preemptive or cooperative + scheduler is being used. */ + #if configUSE_PREEMPTION == 1 + { + extern void ( vPreemptiveTick )( void ); + VIC_VectAddr0 = ( portLONG ) vPreemptiveTick; + } + #else + { + extern void ( vNonPreemptiveTick )( void ); + VIC_VectAddr0 = ( portLONG ) vNonPreemptiveTick; + } + #endif + + VIC_VectCntl0 = VIC_VectCntl_ENABLE | VIC_Channel_Timer0; + + /* Start the timer - interrupts are disabled when this function is called + so it is okay to do this here. */ + T0_TCR = portENABLE_TIMER; +} +/*-----------------------------------------------------------*/ + + + diff --git a/FreeRTOS/portable/GCC/ARM7_LPC2000/portISR.c b/FreeRTOS/portable/GCC/ARM7_LPC2000/portISR.c new file mode 100644 index 0000000..0eed7f4 --- /dev/null +++ b/FreeRTOS/portable/GCC/ARM7_LPC2000/portISR.c @@ -0,0 +1,238 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + + +/*----------------------------------------------------------- + * Components that can be compiled to either ARM or THUMB mode are + * contained in port.c The ISR routines, which can only be compiled + * to ARM mode, are contained in this file. + *----------------------------------------------------------*/ + +/* + Changes from V2.5.2 + + + The critical section management functions have been changed. These no + longer modify the stack and are safe to use at all optimisation levels. + The functions are now also the same for both ARM and THUMB modes. + + Changes from V2.6.0 + + + Removed the 'static' from the definition of vNonPreemptiveTick() to + allow the demo to link when using the cooperative scheduler. + + Changes from V3.2.4 + + + The assembler statements are now included in a single asm block rather + than each line having its own asm block. +*/ + + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Constants required to handle interrupts. */ +#define portTIMER_MATCH_ISR_BIT ( ( unsigned portCHAR ) 0x01 ) +#define portCLEAR_VIC_INTERRUPT ( ( unsigned portLONG ) 0 ) + +/* Constants required to handle critical sections. */ +#define portNO_CRITICAL_NESTING ( ( unsigned portLONG ) 0 ) +volatile unsigned portLONG ulCriticalNesting = 9999UL; + +/*-----------------------------------------------------------*/ + +/* ISR to handle manual context switches (from a call to taskYIELD()). */ +void vPortYieldProcessor( void ) __attribute__((interrupt("SWI"), naked)); + +/* + * The scheduler can only be started from ARM mode, hence the inclusion of this + * function here. + */ +void vPortISRStartFirstTask( void ); +/*-----------------------------------------------------------*/ + +void vPortISRStartFirstTask( void ) +{ + /* Simply start the scheduler. This is included here as it can only be + called from ARM mode. */ + portRESTORE_CONTEXT(); +} +/*-----------------------------------------------------------*/ + +/* + * Called by portYIELD() or taskYIELD() to manually force a context switch. + * + * When a context switch is performed from the task level the saved task + * context is made to look as if it occurred from within the tick ISR. This + * way the same restore context function can be used when restoring the context + * saved from the ISR or that saved from a call to vPortYieldProcessor. + */ +void vPortYieldProcessor( void ) +{ + /* Within an IRQ ISR the link register has an offset from the true return + address, but an SWI ISR does not. Add the offset manually so the same + ISR return code can be used in both cases. */ + asm volatile ( "ADD LR, LR, #4" ); + + /* Perform the context switch. First save the context of the current task. */ + portSAVE_CONTEXT(); + + /* Find the highest priority task that is ready to run. */ + vTaskSwitchContext(); + + /* Restore the context of the new task. */ + portRESTORE_CONTEXT(); +} +/*-----------------------------------------------------------*/ + +/* + * The ISR used for the scheduler tick depends on whether the cooperative or + * the preemptive scheduler is being used. + */ + +#if configUSE_PREEMPTION == 0 + + /* The cooperative scheduler requires a normal IRQ service routine to + simply increment the system tick. */ + void vNonPreemptiveTick( void ) __attribute__ ((interrupt ("IRQ"))); + void vNonPreemptiveTick( void ) + { + vTaskIncrementTick(); + T0_IR = portTIMER_MATCH_ISR_BIT; + VICVectAddr = portCLEAR_VIC_INTERRUPT; + } + +#else + + /* The preemptive scheduler is defined as "naked" as the full context is + saved on entry as part of the context switch. */ + void vPreemptiveTick( void ) __attribute__((naked)); + void vPreemptiveTick( void ) + { + /* Save the context of the interrupted task. */ + portSAVE_CONTEXT(); + + /* Increment the RTOS tick count, then look for the highest priority + task that is ready to run. */ + vTaskIncrementTick(); + vTaskSwitchContext(); + + /* Ready for the next interrupt. */ + T0_IR = portTIMER_MATCH_ISR_BIT; + VIC_VectAddr = portCLEAR_VIC_INTERRUPT; + + /* Restore the context of the new task. */ + portRESTORE_CONTEXT(); + } + +#endif +/*-----------------------------------------------------------*/ + +/* + * The interrupt management utilities can only be called from ARM mode. When + * THUMB_INTERWORK is defined the utilities are defined as functions here to + * ensure a switch to ARM mode. When THUMB_INTERWORK is not defined then + * the utilities are defined as macros in portmacro.h - as per other ports. + */ +#ifdef THUMB_INTERWORK + + void vPortDisableInterruptsFromThumb( void ) __attribute__ ((naked)); + void vPortEnableInterruptsFromThumb( void ) __attribute__ ((naked)); + + void vPortDisableInterruptsFromThumb( void ) + { + asm volatile ( + "STMDB SP!, {R0} \n\t" /* Push R0. */ + "MRS R0, CPSR \n\t" /* Get CPSR. */ + "ORR R0, R0, #0xC0 \n\t" /* Disable IRQ, FIQ. */ + "MSR CPSR, R0 \n\t" /* Write back modified value. */ + "LDMIA SP!, {R0} \n\t" /* Pop R0. */ + "BX R14" ); /* Return back to thumb. */ + } + + void vPortEnableInterruptsFromThumb( void ) + { + asm volatile ( + "STMDB SP!, {R0} \n\t" /* Push R0. */ + "MRS R0, CPSR \n\t" /* Get CPSR. */ + "BIC R0, R0, #0xC0 \n\t" /* Enable IRQ, FIQ. */ + "MSR CPSR, R0 \n\t" /* Write back modified value. */ + "LDMIA SP!, {R0} \n\t" /* Pop R0. */ + "BX R14" ); /* Return back to thumb. */ + } + +#endif /* THUMB_INTERWORK */ + +/* The code generated by the GCC compiler uses the stack in different ways at +different optimisation levels. The interrupt flags can therefore not always +be saved to the stack. Instead the critical section nesting level is stored +in a variable, which is then saved as part of the stack context. */ +void vPortEnterCritical( void ) +{ + /* Disable interrupts as per portDISABLE_INTERRUPTS(); */ + asm volatile ( + "STMDB SP!, {R0} \n\t" /* Push R0. */ + "MRS R0, CPSR \n\t" /* Get CPSR. */ + "ORR R0, R0, #0xC0 \n\t" /* Disable IRQ, FIQ. */ + "MSR CPSR, R0 \n\t" /* Write back modified value. */ + "LDMIA SP!, {R0}" ); /* Pop R0. */ + + /* Now interrupts are disabled ulCriticalNesting can be accessed + directly. Increment ulCriticalNesting to keep a count of how many times + portENTER_CRITICAL() has been called. */ + ulCriticalNesting++; +} + +void vPortExitCritical( void ) +{ + if( ulCriticalNesting > portNO_CRITICAL_NESTING ) + { + /* Decrement the nesting count as we are leaving a critical section. */ + ulCriticalNesting--; + + /* If the nesting level has reached zero then interrupts should be + re-enabled. */ + if( ulCriticalNesting == portNO_CRITICAL_NESTING ) + { + /* Enable interrupts as per portEXIT_CRITICAL(). */ + asm volatile ( + "STMDB SP!, {R0} \n\t" /* Push R0. */ + "MRS R0, CPSR \n\t" /* Get CPSR. */ + "BIC R0, R0, #0xC0 \n\t" /* Enable IRQ, FIQ. */ + "MSR CPSR, R0 \n\t" /* Write back modified value. */ + "LDMIA SP!, {R0}" ); /* Pop R0. */ + } + } +} diff --git a/FreeRTOS/portable/GCC/ARM7_LPC2000/portmacro.h b/FreeRTOS/portable/GCC/ARM7_LPC2000/portmacro.h new file mode 100644 index 0000000..3051577 --- /dev/null +++ b/FreeRTOS/portable/GCC/ARM7_LPC2000/portmacro.h @@ -0,0 +1,267 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* + Changes from V3.2.3 + + + Modified portENTER_SWITCHING_ISR() to allow use with GCC V4.0.1. + + Changes from V3.2.4 + + + Removed the use of the %0 parameter within the assembler macros and + replaced them with hard coded registers. This will ensure the + assembler does not select the link register as the temp register as + was occasionally happening previously. + + + The assembler statements are now included in a single asm block rather + than each line having its own asm block. +*/ + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE unsigned portLONG +#define portBASE_TYPE portLONG + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef unsigned portSHORT portTickType; + #define portMAX_DELAY ( portTickType ) 0xffff +#else + typedef unsigned portLONG portTickType; + #define portMAX_DELAY ( portTickType ) 0xffffffff +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 4 +#define portNOP() asm volatile ( "NOP" ); +/*-----------------------------------------------------------*/ + + +/* Scheduler utilities. */ + +/* + * portRESTORE_CONTEXT, portRESTORE_CONTEXT, portENTER_SWITCHING_ISR + * and portEXIT_SWITCHING_ISR can only be called from ARM mode, but + * are included here for efficiency. An attempt to call one from + * THUMB mode code will result in a compile time error. + */ + +#define portRESTORE_CONTEXT() \ +{ \ +extern volatile void * volatile pxCurrentTCB; \ +extern volatile unsigned portLONG ulCriticalNesting; \ + \ + /* Set the LR to the task stack. */ \ + asm volatile ( \ + "LDR R0, =pxCurrentTCB \n\t" \ + "LDR R0, [R0] \n\t" \ + "LDR LR, [R0] \n\t" \ + \ + /* The critical nesting depth is the first item on the stack. */ \ + /* Load it into the ulCriticalNesting variable. */ \ + "LDR R0, =ulCriticalNesting \n\t" \ + "LDMFD LR!, {R1} \n\t" \ + "STR R1, [R0] \n\t" \ + \ + /* Get the SPSR from the stack. */ \ + "LDMFD LR!, {R0} \n\t" \ + "MSR SPSR, R0 \n\t" \ + \ + /* Restore all system mode registers for the task. */ \ + "LDMFD LR, {R0-R14}^ \n\t" \ + "NOP \n\t" \ + \ + /* Restore the return address. */ \ + "LDR LR, [LR, #+60] \n\t" \ + \ + /* And return - correcting the offset in the LR to obtain the */ \ + /* correct address. */ \ + "SUBS PC, LR, #4 \n\t" \ + ); \ + ( void ) ulCriticalNesting; \ + ( void ) pxCurrentTCB; \ +} +/*-----------------------------------------------------------*/ + +#define portSAVE_CONTEXT() \ +{ \ +extern volatile void * volatile pxCurrentTCB; \ +extern volatile unsigned portLONG ulCriticalNesting; \ + \ + /* Push R0 as we are going to use the register. */ \ + asm volatile ( \ + "STMDB SP!, {R0} \n\t" \ + \ + /* Set R0 to point to the task stack pointer. */ \ + "STMDB SP,{SP}^ \n\t" \ + "NOP \n\t" \ + "SUB SP, SP, #4 \n\t" \ + "LDMIA SP!,{R0} \n\t" \ + \ + /* Push the return address onto the stack. */ \ + "STMDB R0!, {LR} \n\t" \ + \ + /* Now we have saved LR we can use it instead of R0. */ \ + "MOV LR, R0 \n\t" \ + \ + /* Pop R0 so we can save it onto the system mode stack. */ \ + "LDMIA SP!, {R0} \n\t" \ + \ + /* Push all the system mode registers onto the task stack. */ \ + "STMDB LR,{R0-LR}^ \n\t" \ + "NOP \n\t" \ + "SUB LR, LR, #60 \n\t" \ + \ + /* Push the SPSR onto the task stack. */ \ + "MRS R0, SPSR \n\t" \ + "STMDB LR!, {R0} \n\t" \ + \ + "LDR R0, =ulCriticalNesting \n\t" \ + "LDR R0, [R0] \n\t" \ + "STMDB LR!, {R0} \n\t" \ + \ + /* Store the new top of stack for the task. */ \ + "LDR R0, =pxCurrentTCB \n\t" \ + "LDR R0, [R0] \n\t" \ + "STR LR, [R0] \n\t" \ + ); \ + ( void ) ulCriticalNesting; \ + ( void ) pxCurrentTCB; \ +} + + +/*----------------------------------------------------------- + * ISR entry and exit macros. These are only required if a task switch + * is required from the ISR. + *----------------------------------------------------------*/ + + +#define portENTER_SWITCHING_ISR() \ + /* Save the context of the interrupted task. */ \ + portSAVE_CONTEXT(); \ + \ + /* We don't know the stack requirements for the ISR, so the frame */\ + /* pointer will be set to the top of the task stack, and the stack*/\ + /* pointer left where it is. The IRQ stack will get used for any */\ + /* functions calls made by this ISR. */ \ + asm volatile ( "SUB R11, LR, #4" ); \ + { + +#define portEXIT_SWITCHING_ISR( SwitchRequired ) \ + /* If a switch is required then we just need to call */ \ + /* vTaskSwitchContext() as the context has already been */ \ + /* saved. */ \ + if( SwitchRequired ) \ + { \ + vTaskSwitchContext(); \ + } \ + } \ + /* Restore the context of which ever task is now the highest */ \ + /* priority that is ready to run. */ \ + portRESTORE_CONTEXT(); + +#define portYIELD() asm volatile ( "SWI" ); +/*-----------------------------------------------------------*/ + + +/* Critical section management. */ + +/* + * The interrupt management utilities can only be called from ARM mode. When + * THUMB_INTERWORK is defined the utilities are defined as functions in + * portISR.c to ensure a switch to ARM mode. When THUMB_INTERWORK is not + * defined then the utilities are defined as macros here - as per other ports. + */ + +#ifdef THUMB_INTERWORK + + extern void vPortDisableInterruptsFromThumb( void ) __attribute__ ((naked)); + extern void vPortEnableInterruptsFromThumb( void ) __attribute__ ((naked)); + + #define portDISABLE_INTERRUPTS() vPortDisableInterruptsFromThumb() + #define portENABLE_INTERRUPTS() vPortEnableInterruptsFromThumb() + +#else + + #define portDISABLE_INTERRUPTS() \ + asm volatile ( \ + "STMDB SP!, {R0} \n\t" /* Push R0. */ \ + "MRS R0, CPSR \n\t" /* Get CPSR. */ \ + "ORR R0, R0, #0xC0 \n\t" /* Disable IRQ, FIQ. */ \ + "MSR CPSR, R0 \n\t" /* Write back modified value. */ \ + "LDMIA SP!, {R0} " ) /* Pop R0. */ + + #define portENABLE_INTERRUPTS() \ + asm volatile ( \ + "STMDB SP!, {R0} \n\t" /* Push R0. */ \ + "MRS R0, CPSR \n\t" /* Get CPSR. */ \ + "BIC R0, R0, #0xC0 \n\t" /* Enable IRQ, FIQ. */ \ + "MSR CPSR, R0 \n\t" /* Write back modified value. */ \ + "LDMIA SP!, {R0} " ) /* Pop R0. */ + +#endif /* THUMB_INTERWORK */ + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portENTER_CRITICAL() vPortEnterCritical(); +#define portEXIT_CRITICAL() vPortExitCritical(); +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) __attribute__ ((naked)) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) + +#endif /* PORTMACRO_H */ + diff --git a/FreeRTOS/portable/GCC/Makefile b/FreeRTOS/portable/GCC/Makefile new file mode 100644 index 0000000..0c85d6f --- /dev/null +++ b/FreeRTOS/portable/GCC/Makefile @@ -0,0 +1,9 @@ +# +# Define all object files. +# +SUBDIRS=ARM7_LPC2000 + +.PHONY: all +all: + @for i in $(SUBDIRS); do \ + (cd $$i; $(MAKE) $(MFLAGS) $(MYMAKEFLAGS) all); done diff --git a/FreeRTOS/portable/Makefile b/FreeRTOS/portable/Makefile new file mode 100644 index 0000000..b7f40b0 --- /dev/null +++ b/FreeRTOS/portable/Makefile @@ -0,0 +1,9 @@ +# +# Define all object files. +# +SUBDIRS=GCC MemMang + +.PHONY: all +all: + @for i in $(SUBDIRS); do \ + (cd $$i; $(MAKE) $(MFLAGS) $(MYMAKEFLAGS) all); done diff --git a/FreeRTOS/portable/MemMang/Makefile b/FreeRTOS/portable/MemMang/Makefile new file mode 100644 index 0000000..d77059a --- /dev/null +++ b/FreeRTOS/portable/MemMang/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=heap_2.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/FreeRTOS/portable/MemMang/heap_1.c b/FreeRTOS/portable/MemMang/heap_1.c new file mode 100644 index 0000000..e65eeaa --- /dev/null +++ b/FreeRTOS/portable/MemMang/heap_1.c @@ -0,0 +1,140 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* + +Changes between V2.5.1 and V2.5.1 + + + The memory pool has been defined within a struct to ensure correct memory + alignment on 32bit systems. + +Changes between V2.6.1 and V3.0.0 + + + An overflow check has been added to ensure the next free byte variable + does not wrap around. +*/ + + +/* + * The simplest possible implementation of pvPortMalloc(). Note that this + * implementation does NOT allow allocated memory to be freed again. + * + * See heap_2.c and heap_3.c for alternative implementations, and the memory + * management pages of http://www.FreeRTOS.org for more information. + */ +#include +#include "FreeRTOS.h" +#include "task.h" + +/* Setup the correct byte alignment mask for the defined byte alignment. */ + +#if portBYTE_ALIGNMENT == 8 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0000 ) +#endif + +#ifndef heapBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +/* Allocate the memory for the heap. The struct is used to force byte +alignment without using any non-portable code. */ +static struct xRTOS_HEAP +{ + unsigned portLONG ulDummy; + unsigned portCHAR ucHeap[ configTOTAL_HEAP_SIZE ]; +} xHeap; + +static size_t xNextFreeByte = ( size_t ) 0; +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn = NULL; + + /* Ensure that blocks are always aligned to the required number of bytes. */ + #if portBYTE_ALIGNMENT != 1 + if( xWantedSize & heapBYTE_ALIGNMENT_MASK ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & heapBYTE_ALIGNMENT_MASK ) ); + } + #endif + + vTaskSuspendAll(); + { + /* Check there is enough room left for the allocation. */ + if( ( ( xNextFreeByte + xWantedSize ) < configTOTAL_HEAP_SIZE ) && + ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */ + { + /* Return the next free byte then increment the index past this + block. */ + pvReturn = &( xHeap.ucHeap[ xNextFreeByte ] ); + xNextFreeByte += xWantedSize; + } + } + xTaskResumeAll(); + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + /* Memory cannot be freed using this scheme. See heap_2.c and heap_3.c + for alternative implementations, and the memory management pages of + http://www.FreeRTOS.org for more information. */ + ( void ) pv; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* Only required when static memory is not cleared. */ + xNextFreeByte = ( size_t ) 0; +} + + diff --git a/FreeRTOS/portable/MemMang/heap_2.c b/FreeRTOS/portable/MemMang/heap_2.c new file mode 100644 index 0000000..3d51a7c --- /dev/null +++ b/FreeRTOS/portable/MemMang/heap_2.c @@ -0,0 +1,271 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that permits + * allocated blocks to be freed, but does not combine adjacent free blocks + * into a single larger block. + * + * See heap_1.c and heap_3.c for alternative implementations, and the memory + * management pages of http://www.FreeRTOS.org for more information. + */ +#include + +#include "FreeRTOS.h" +#include "task.h" + +/* Setup the correct byte alignment mask for the defined byte alignment. */ + +#if portBYTE_ALIGNMENT == 8 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define heapBYTE_ALIGNMENT_MASK ( ( size_t ) 0x0000 ) +#endif + +#ifndef heapBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +/* Allocate the memory for the heap. The struct is used to force byte +alignment without using any non-portable code. */ +static struct xRTOS_HEAP +{ + unsigned portLONG ulDummy; + unsigned portCHAR ucHeap[ configTOTAL_HEAP_SIZE ]; +} xHeap; + +/* Define the linked list structure. This is used to link free blocks in order +of their size. */ +typedef struct A_BLOCK_LINK +{ + struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ + size_t xBlockSize; /*<< The size of the free block. */ +} xBlockLink; + + +static const unsigned portSHORT heapSTRUCT_SIZE = ( sizeof( xBlockLink ) + ( sizeof( xBlockLink ) % portBYTE_ALIGNMENT ) ); +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) + +/* Create a couple of list links to mark the start and end of the list. */ +static xBlockLink xStart, xEnd; + +/* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */ + +/* + * Insert a block into the list of free blocks - which is ordered by size of + * the block. Small blocks at the start of the list and large blocks at the end + * of the list. + */ +#define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ +{ \ +xBlockLink *pxIterator; \ +size_t xBlockSize; \ + \ + xBlockSize = pxBlockToInsert->xBlockSize; \ + \ + /* Iterate through the list until a block is found that has a larger size */ \ + /* than the block we are inserting. */ \ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \ + { \ + /* There is nothing to do here - just iterate to the correct position. */ \ + } \ + \ + /* Update the list to include the block being inserted in the correct */ \ + /* position. */ \ + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \ + pxIterator->pxNextFreeBlock = pxBlockToInsert; \ +} +/*-----------------------------------------------------------*/ + +#define prvHeapInit() \ +{ \ +xBlockLink *pxFirstFreeBlock; \ + \ + /* xStart is used to hold a pointer to the first item in the list of free */ \ + /* blocks. The void cast is used to prevent compiler warnings. */ \ + xStart.pxNextFreeBlock = ( void * ) xHeap.ucHeap; \ + xStart.xBlockSize = ( size_t ) 0; \ + \ + /* xEnd is used to mark the end of the list of free blocks. */ \ + xEnd.xBlockSize = configTOTAL_HEAP_SIZE; \ + xEnd.pxNextFreeBlock = NULL; \ + \ + /* To start with there is a single free block that is sized to take up the \ + entire heap space. */ \ + pxFirstFreeBlock = ( void * ) xHeap.ucHeap; \ + pxFirstFreeBlock->xBlockSize = configTOTAL_HEAP_SIZE; \ + pxFirstFreeBlock->pxNextFreeBlock = &xEnd; \ +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vPortUsedMem == 1 ) + +void vPortUsedMem(int *bytesFree, int *bytesUsed, int *blocksFree) +{ + xBlockLink *pxBlock = &xStart; + + *bytesFree = 0; + *bytesUsed = 0; + *blocksFree = 0; + + vTaskSuspendAll(); + + while ((pxBlock) && (pxBlock != &xEnd)) + { + *bytesFree += pxBlock->xBlockSize; + pxBlock = pxBlock->pxNextFreeBlock; + ++*blocksFree; + } + + xTaskResumeAll(); + + *bytesUsed = configTOTAL_HEAP_SIZE - *bytesFree; +} + +#endif +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ + xBlockLink *pxBlock, *pxPreviousBlock, *pxNewBlockLink; + static portBASE_TYPE xHeapHasBeenInitialised = pdFALSE; + void *pvReturn = NULL; + + vTaskSuspendAll(); + { + /* If this is the first call to malloc then the heap will require + initialisation to setup the list of free blocks. */ + if( xHeapHasBeenInitialised == pdFALSE ) + { + prvHeapInit(); + xHeapHasBeenInitialised = pdTRUE; + } + + /* The wanted size is increased so it can contain a xBlockLink + structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += heapSTRUCT_SIZE; + + /* Ensure that blocks are always aligned to the required number of bytes. */ + if( xWantedSize & heapBYTE_ALIGNMENT_MASK ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & heapBYTE_ALIGNMENT_MASK ) ); + } + } + + if( ( xWantedSize > 0 ) && ( xWantedSize < configTOTAL_HEAP_SIZE ) ) + { + /* Blocks are stored in byte order - traverse the list from the start + (smallest) block until one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If we found the end marker then a block of adequate size was not found. */ + if( pxBlock != &xEnd ) + { + /* Return the memory space - jumping over the xBlockLink structure + at its start. */ + pvReturn = ( void * ) ( ( ( unsigned portCHAR * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE ); + + /* This block is being returned for use so must be taken our of the + list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into two. */ + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new block + following the number of bytes requested. The void cast is + used to prevent byte alignment warnings from the compiler. */ + pxNewBlockLink = ( void * ) ( ( ( unsigned portCHAR * ) pxBlock ) + xWantedSize ); + + /* Calculate the sizes of two blocks split from the single + block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); + } + } + } + } + xTaskResumeAll(); + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ +unsigned portCHAR *puc = ( unsigned portCHAR * ) pv; +xBlockLink *pxLink; + + if( pv ) + { + /* The memory being freed will have an xBlockLink structure immediately + before it. */ + puc -= heapSTRUCT_SIZE; + + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + prvInsertBlockIntoFreeList( ( ( xBlockLink * ) pxLink ) ); + } + xTaskResumeAll(); + } +} +/*-----------------------------------------------------------*/ + diff --git a/FreeRTOS/portable/MemMang/heap_3.c b/FreeRTOS/portable/MemMang/heap_3.c new file mode 100644 index 0000000..d5b55c3 --- /dev/null +++ b/FreeRTOS/portable/MemMang/heap_3.c @@ -0,0 +1,82 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + + +/* + * Implementation of pvPortMalloc() and vPortFree() that relies on the + * compilers own malloc() and free() implementations. + * + * This file can only be used if the linker is configured to to generate + * a heap memory area. + * + * See heap_2.c and heap_1.c for alternative implementations, and the memory + * management pages of http://www.FreeRTOS.org for more information. + */ + +#include + +#include "FreeRTOS.h" +#include "task.h" + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn; + + vTaskSuspendAll(); + { + pvReturn = malloc( xWantedSize ); + } + xTaskResumeAll(); + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + if( pv ) + { + vTaskSuspendAll(); + { + free( pv ); + } + xTaskResumeAll(); + } +} + + + diff --git a/FreeRTOS/queue.c b/FreeRTOS/queue.c new file mode 100644 index 0000000..ab0c13a --- /dev/null +++ b/FreeRTOS/queue.c @@ -0,0 +1,930 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +/* +Changes from V1.01 + + + More use of 8bit data types. + + Function name prefixes changed where the data type returned has changed. + +Changed from V2.0.0 + + + Added the queue locking mechanism and make more use of the scheduler + suspension feature to minimise the time interrupts have to be disabled + when accessing a queue. + +Changed from V2.2.0 + + + Explicit use of 'signed' qualifier on portCHAR types added. + +Changes from V3.0.0 + + + API changes as described on the FreeRTOS.org WEB site. + +Changes from V3.2.3 + + + Added the queue functions that can be used from co-routines. + +Changes from V4.0.5 + + + Added a loop within xQueueSend() and xQueueReceive() to prevent the + functions exiting when a block time remains and the function has + not completed. + +Changes from V4.1.2: + + + BUG FIX: Removed the call to prvIsQueueEmpty from within xQueueCRReceive + as it exited with interrupts enabled. Thanks Paul Katz. + +Changes from V4.1.3: + + + Modified xQueueSend() and xQueueReceive() to handle the (very unlikely) + case whereby a task unblocking due to a temporal event can remove/send an + item from/to a queue when a higher priority task is still blocked on the + queue. This modification is a result of the SafeRTOS testing. +*/ + +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "croutine.h" + +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +/* Constants used with the cRxLock and cTxLock structure members. */ +#define queueUNLOCKED ( ( signed portBASE_TYPE ) -1 ) +#define queueERRONEOUS_UNBLOCK ( -1 ) + +/* + * Definition of the queue used by the scheduler. + * Items are queued by copy, not reference. + */ +typedef struct QueueDefinition +{ + signed portCHAR *pcHead; /*< Points to the beginning of the queue storage area. */ + signed portCHAR *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + + signed portCHAR *pcWriteTo; /*< Points to the free next place in the storage area. */ + signed portCHAR *pcReadFrom; /*< Points to the last place that a queued item was read from. */ + + xList xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + xList xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + unsigned portBASE_TYPE uxMessagesWaiting;/*< The number of items currently in the queue. */ + unsigned portBASE_TYPE uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + unsigned portBASE_TYPE uxItemSize; /*< The size of each items that the queue will hold. */ + + signed portBASE_TYPE xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + signed portBASE_TYPE xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ +} xQUEUE; +/*-----------------------------------------------------------*/ + +/* + * Inside this file xQueueHandle is a pointer to a xQUEUE structure. + * To keep the definition private the API header file defines it as a + * pointer to void. + */ +typedef xQUEUE * xQueueHandle; + +/* + * Prototypes for public functions are included here so we don't have to + * include the API header file (as it defines xQueueHandle differently). These + * functions are documented in the API header file. + */ +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); +signed portBASE_TYPE xQueueSend( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait ); +unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle pxQueue ); +void vQueueDelete( xQueueHandle xQueue ); +signed portBASE_TYPE xQueueSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken ); +signed portBASE_TYPE xQueueReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ); +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ); + +#if configUSE_CO_ROUTINES == 1 + signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ); + signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ); + signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ); + signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ); +#endif + +/* + * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not + * prevent an ISR from adding or removing items to the queue, but does prevent + * an ISR from removing tasks from the queue event lists. If an ISR finds a + * queue is locked it will instead increment the appropriate queue lock count + * to indicate that a task may require unblocking. When the queue in unlocked + * these lock counts are inspected, and the appropriate action taken. + */ +static void prvUnlockQueue( xQueueHandle pxQueue ); + +/* + * Uses a critical section to determine if there is any data in a queue. + * + * @return pdTRUE if the queue contains no items, otherwise pdFALSE. + */ +static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue ); + +/* + * Uses a critical section to determine if there is any space in a queue. + * + * @return pdTRUE if there is no space, otherwise pdFALSE; + */ +static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue ); + +/* + * Macro that copies an item into the queue. This is done by copying the item + * byte for byte, not by reference. Updates the queue state to ensure it's + * integrity after the copy. + */ +#define prvCopyQueueData( pxQueue, pvItemToQueue ) \ +{ \ + memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize ); \ + ++( pxQueue->uxMessagesWaiting ); \ + pxQueue->pcWriteTo += pxQueue->uxItemSize; \ + if( pxQueue->pcWriteTo >= pxQueue->pcTail ) \ + { \ + pxQueue->pcWriteTo = pxQueue->pcHead; \ + } \ +} +/*-----------------------------------------------------------*/ + +/* + * Macro to mark a queue as locked. Locking a queue prevents an ISR from + * accessing the queue event lists. + */ +#define prvLockQueue( pxQueue ) \ +{ \ + taskENTER_CRITICAL(); \ + ++( pxQueue->xRxLock ); \ + ++( pxQueue->xTxLock ); \ + taskEXIT_CRITICAL(); \ +} +/*-----------------------------------------------------------*/ + + +/*----------------------------------------------------------- + * PUBLIC QUEUE MANAGEMENT API documented in queue.h + *----------------------------------------------------------*/ + +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ) +{ +xQUEUE *pxNewQueue; +size_t xQueueSizeInBytes; + + /* Allocate the new queue structure. */ + if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 ) + { + pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) ); + if( pxNewQueue != NULL ) + { + /* Create the list of pointers to queue items. The queue is one byte + longer than asked for to make wrap checking easier/faster. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; + + pxNewQueue->pcHead = ( signed portCHAR * ) pvPortMalloc( xQueueSizeInBytes ); + if( pxNewQueue->pcHead != NULL ) + { + /* Initialise the queue members as described above where the + queue type is defined. */ + pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize ); + pxNewQueue->uxMessagesWaiting = 0; + pxNewQueue->pcWriteTo = pxNewQueue->pcHead; + pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - 1 ) * uxItemSize ); + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + /* Likewise ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + return pxNewQueue; + } + else + { + vPortFree( pxNewQueue ); + } + } + } + + /* Will only reach here if we could not allocate enough memory or no memory + was required. */ + return NULL; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn = pdPASS; +xTimeOutType xTimeOut; + + /* Make sure other tasks do not access the queue. */ + vTaskSuspendAll(); + + /* Capture the current time status for future reference. */ + vTaskSetTimeOutState( &xTimeOut ); + + /* It is important that this is the only thread/ISR that modifies the + ready or delayed lists until xTaskResumeAll() is called. Places where + the ready/delayed lists are modified include: + + + vTaskDelay() - Nothing can call vTaskDelay as the scheduler is + suspended, vTaskDelay() cannot be called from an ISR. + + vTaskPrioritySet() - Has a critical section around the access. + + vTaskSwitchContext() - This will not get executed while the scheduler + is suspended. + + prvCheckDelayedTasks() - This will not get executed while the + scheduler is suspended. + + xTaskCreate() - Has a critical section around the access. + + vTaskResume() - Has a critical section around the access. + + xTaskResumeAll() - Has a critical section around the access. + + xTaskRemoveFromEventList - Checks to see if the scheduler is + suspended. If so then the TCB being removed from the event is + removed from the event and added to the xPendingReadyList. + */ + + /* Make sure interrupts do not access the queue event list. */ + prvLockQueue( pxQueue ); + + /* It is important that interrupts to not access the event list of the + queue being modified here. Places where the event list is modified + include: + + + xQueueSendFromISR(). This checks the lock on the queue to see if + it has access. If the queue is locked then the Tx lock count is + incremented to signify that a task waiting for data can be made ready + once the queue lock is removed. If the queue is not locked then + a task can be moved from the event list, but will not be removed + from the delayed list or placed in the ready list until the scheduler + is unlocked. + + + xQueueReceiveFromISR(). As per xQueueSendFromISR(). + */ + + /* If the queue is already full we may have to block. */ + do + { + if( prvIsQueueFull( pxQueue ) ) + { + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* We are going to place ourselves on the xTasksWaitingToSend event + list, and will get woken should the delay expire, or space become + available on the queue. + + As detailed above we do not require mutual exclusion on the event + list as nothing else can modify it or the ready lists while we + have the scheduler suspended and queue locked. + + It is possible that an ISR has removed data from the queue since we + checked if any was available. If this is the case then the data + will have been copied from the queue, and the queue variables + updated, but the event list will not yet have been checked to see if + anything is waiting as the queue is locked. */ + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Force a context switch now as we are blocked. We can do + this from within a critical section as the task we are + switching to has its own context. When we return here (i.e. we + unblock) we will leave the critical section as normal. + + It is possible that an ISR has caused an event on an unrelated and + unlocked queue. If this was the case then the event list for that + queue will have been updated but the ready lists left unchanged - + instead the readied task will have been added to the pending ready + list. */ + taskENTER_CRITICAL(); + { + /* We can safely unlock the queue and scheduler here as + interrupts are disabled. We must not yield with anything + locked, but we can yield from within a critical section. + + Tasks that have been placed on the pending ready list cannot + be tasks that are waiting for events on this queue. See + in comment xTaskRemoveFromEventList(). */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler may cause a yield. If so then there + is no point yielding again here. */ + if( !xTaskResumeAll() ) + { + taskYIELD(); + } + + /* We want to check to see if the queue is still full + before leaving the critical section. This is to prevent + this task placing an item into the queue due to an + interrupt making space on the queue between critical + sections (when there might be a higher priority task + blocked on the queue that cannot run yet because the + scheduler gets suspended). */ + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + /* We unblocked but there is no space in the queue, + we probably timed out. */ + xReturn = errQUEUE_FULL; + } + + /* Before leaving the critical section we have to ensure + exclusive access again. */ + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + } + taskEXIT_CRITICAL(); + } + } + + /* If xReturn is errQUEUE_FULL then we unblocked when the queue + was still full. Don't check it again now as it is possible that + an interrupt has removed an item from the queue since we left the + critical section and we don't want to write to the queue in case + there is a task of higher priority blocked waiting for space to + be available on the queue. If this is the case the higher priority + task will execute when the scheduler is unsupended. */ + if( xReturn != errQUEUE_FULL ) + { + /* When we are here it is possible that we unblocked as space became + available on the queue. It is also possible that an ISR posted to the + queue since we left the critical section, so it may be that again there + is no space. This would only happen if a task and ISR post onto the + same queue. */ + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyQueueData( pxQueue, pvItemToQueue ); + xReturn = pdPASS; + + /* Update the TxLock count so prvUnlockQueue knows to check for + tasks waiting for data to become available in the queue. */ + ++( pxQueue->xTxLock ); + } + else + { + xReturn = errQUEUE_FULL; + } + } + taskEXIT_CRITICAL(); + } + + if( xReturn == errQUEUE_FULL ) + { + if( xTicksToWait > 0 ) + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + xReturn = queueERRONEOUS_UNBLOCK; + } + } + } + } + while( xReturn == queueERRONEOUS_UNBLOCK ); + + prvUnlockQueue( pxQueue ); + xTaskResumeAll(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken ) +{ + /* Similar to xQueueSend, except we don't block if there is no room in the + queue. Also we don't directly wake a task that was blocked on a queue + read, instead we return a flag to say whether a context switch is required + or not (i.e. has a task with a higher priority than us been woken by this + post). */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyQueueData( pxQueue, pvItemToQueue ); + + /* If the queue is locked we do not alter the event list. This will + be done when the queue is unlocked later. */ + if( pxQueue->xTxLock == queueUNLOCKED ) + { + /* We only want to wake one task per ISR, so check that a task has + not already been woken. */ + if( !xTaskPreviouslyWoken ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + return pdTRUE; + } + } + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was posted while it was locked. */ + ++( pxQueue->xTxLock ); + } + } + + return xTaskPreviouslyWoken; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn = pdTRUE; +xTimeOutType xTimeOut; + + /* This function is very similar to xQueueSend(). See comments within + xQueueSend() for a more detailed explanation. + + Make sure other tasks do not access the queue. */ + vTaskSuspendAll(); + + /* Capture the current time status for future reference. */ + vTaskSetTimeOutState( &xTimeOut ); + + /* Make sure interrupts do not access the queue. */ + prvLockQueue( pxQueue ); + + do + { + /* If there are no messages in the queue we may have to block. */ + if( prvIsQueueEmpty( pxQueue ) ) + { + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + taskENTER_CRITICAL(); + { + prvUnlockQueue( pxQueue ); + if( !xTaskResumeAll() ) + { + taskYIELD(); + } + + if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ) + { + /* We unblocked but the queue is empty. We probably + timed out. */ + xReturn = errQUEUE_EMPTY; + } + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + } + taskEXIT_CRITICAL(); + } + } + + if( xReturn != errQUEUE_EMPTY ) + { + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + /* Increment the lock count so prvUnlockQueue knows to check for + tasks waiting for space to become available on the queue. */ + ++( pxQueue->xRxLock ); + xReturn = pdPASS; + } + else + { + xReturn = errQUEUE_EMPTY; + } + } + taskEXIT_CRITICAL(); + } + + if( xReturn == errQUEUE_EMPTY ) + { + if( xTicksToWait > 0 ) + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + xReturn = queueERRONEOUS_UNBLOCK; + } + } + } + } while( xReturn == queueERRONEOUS_UNBLOCK ); + + /* We no longer require exclusive access to the queue. */ + prvUnlockQueue( pxQueue ); + xTaskResumeAll(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ) +{ +signed portBASE_TYPE xReturn; + + /* We cannot block from an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + /* If the queue is locked we will not modify the event list. Instead + we update the lock count so the task that unlocks the queue will know + that an ISR has removed data while the queue was locked. */ + if( pxQueue->xRxLock == queueUNLOCKED ) + { + /* We only want to wake one task per ISR, so check that a task has + not already been woken. */ + if( !( *pxTaskWoken ) ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + force a context switch. */ + *pxTaskWoken = pdTRUE; + } + } + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was removed while it was locked. */ + ++( pxQueue->xRxLock ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle pxQueue ) +{ +unsigned portBASE_TYPE uxReturn; + + taskENTER_CRITICAL(); + uxReturn = pxQueue->uxMessagesWaiting; + taskEXIT_CRITICAL(); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +void vQueueDelete( xQueueHandle pxQueue ) +{ + vPortFree( pxQueue->pcHead ); + vPortFree( pxQueue ); +} +/*-----------------------------------------------------------*/ + +static void prvUnlockQueue( xQueueHandle pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + removed from the queue while the queue was locked. When a queue is + locked items can be added or removed, but the event lists cannot be + updated. */ + taskENTER_CRITICAL(); + { + --( pxQueue->xTxLock ); + + /* See if data was added to the queue while it was locked. */ + if( pxQueue->xTxLock > queueUNLOCKED ) + { + pxQueue->xTxLock = queueUNLOCKED; + + /* Data was posted while the queue was locked. Are any tasks + blocked waiting for data to become available? */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + vTaskMissedYield(); + } + } + } + } + taskEXIT_CRITICAL(); + + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + --( pxQueue->xRxLock ); + + if( pxQueue->xRxLock > queueUNLOCKED ) + { + pxQueue->xRxLock = queueUNLOCKED; + + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + } + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + taskENTER_CRITICAL(); + xReturn = ( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ); + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + taskENTER_CRITICAL(); + xReturn = ( pxQueue->uxMessagesWaiting == pxQueue->uxLength ); + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn; + + /* If the queue is already full we may have to block. A critical section + is required to prevent an interrupt removing something from the queue + between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) ) + { + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portNOP(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyQueueData( pxQueue, pvItemToQueue ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechansim is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + } + } + else + { + xReturn = errQUEUE_FULL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn; + + /* If the queue is already empty we may have to block. A critical section + is required to prevent an interrupt adding something to the queue + between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portNOP(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechansim is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + } + } + else + { + xReturn = pdFAIL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + + + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ) +{ + /* Cannot block within an ISR so if there is no space on the queue then + exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyQueueData( pxQueue, pvItemToQueue ); + + /* We only want to wake one co-routine per ISR, so check that a + co-routine has not already been woken. */ + if( !xCoRoutinePreviouslyWoken ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + } + } + } + + return xCoRoutinePreviouslyWoken; +} +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxCoRoutineWoken ) +{ +signed portBASE_TYPE xReturn; + + /* We cannot block from an ISR, so check there is data available. If + not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( !( *pxCoRoutineWoken ) ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + } + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + diff --git a/FreeRTOS/tasks.c b/FreeRTOS/tasks.c new file mode 100644 index 0000000..3f2f067 --- /dev/null +++ b/FreeRTOS/tasks.c @@ -0,0 +1,1936 @@ +/* + FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version, along + with development and support options. + *************************************************************************** +*/ + +/* +Changes from V1.00: + + + Call to portRESTORE_CONTEXT has been removed. The first context + switch is now performed within sPortStartScheduler(). + +Changes from V1.01: + + + More use of 8bit data types. + + Function name prefixes changed where the data type returned has changed. + + configUSE_TRACE_FACILITY is no longer defined by default. + +Changes from V1.2.0 + + + Introduced ucTopReadyPriority. This tracks the highest priority ready + queue that contains a valid TCB and thus makes the context switch + slightly faster. + + + prvAddTaskToReadyQueue() has been made a macro. + +Changes from V1.2.6 + + + Added conditional compilation directives. + + Extended API. + + Rearranged function order. + + Creating a task now causes a context switch if the task being created + has a higher priority than the calling task - assuming the kernel is + running. + + vTaskDelete() now only causes a context switch if the calling task is + the task being deleted. + +Changes from V2.0.0 + + + Allow the type of the tick count to be 16 or 32 bits. + + Introduce xPendingReadyList feature to allow the time interrupts have to + be disabled to be minimised. + + Remove the #if( INCLUDE_vTaskSuspendAll ) statements. vTaskSuspendAll() + is now always included as it is used by the scheduler itself. + +Changes from V2.1.0 + + + Bug fix - pxCurrentTCB is now initialised before the call to + prvInitializeTaskLists(). Previously pxCurrentTCB could be accessed + while null. + +Changed from V2.1.1 + + + Change to where lStackSize is declared within sTaskCreate() to prevent + compiler warnings with 8051 port. + +Changes from V2.2.0 + + + Explicit use of 'signed' qualifier on portCHAR types added. + + Changed odd calculation of initial pxTopOfStack value when + portSTACK_GROWTH < 0. + + Removed pcVersionNumber definition. + +Changes from V2.5.3 + + + cTaskResumeAll() modified to ensure it can be called prior to the task + lists being initialised. + +Changes from V2.5.5 + + + Added API function vTaskDelayUntil(). + + Added INCLUDE_vTaskDelay conditional compilation. + +Changes from V2.6.0 + + + Updated the vWriteTraceToBuffer macro to always be 4 byte aligned so it + can be used on ARM architectures. + + tskMAX_TASK_NAME_LEN definition replaced with the port specific + configMAX_TASK_NAME_LEN definition. + + Removed the call to strcpy when copying across the task name into the + TCB. + + Added ucTasksDeleted variable to prevent vTaskSuspendAll() being called + too often in the idle task. + +Changes between V3.0.0 and V2.6.1 + + + When resuming the scheduler a yield is performed if either a tick has + been missed, or a task is moved from the pending ready list into a ready + list. Previously a yield was not performed on this second condition. + + Introduced the type portBASE_TYPE. This necessitates several API + changes. + + Removed the sUsingPreemption variable. The constant defined in + portmacro.h is now used directly. + + The idle task can now include an optional hook function - and no longer + completes its time slice if other tasks with equal priority to it are + ready to run. + + See the FreeRTOS.org documentation for more information on V2.x.x to + V3.x.x modifications. + +Changes from V3.1.1 + + + Modified vTaskPrioritySet() and vTaskResume() to allow these functions to + be called while the scheduler is suspended. + + Corrected the task ordering within event lists. + +Changes from V3.2.0 + + + Added function xTaskGetCurrentTaskHandle(). + +Changes from V3.2.4 + + + Changed the volatile declarations on some variables to reflect the + changes to the list definitions. + + Changed the order of the TCB definition so there is commonality between + the task control block and a co-routine control block. + + Allow the scheduler to be started even if no tasks other than the idle + task has been created. This allows co-routines to run even when no tasks + have been created. + + The need for a context switch is now signalled if a task woken by an + event has a priority greater or equal to the currently running task. + Previously this was only greater than. + +Changes from V4.0.0 + + + Added the xMissedYield handling. + +Changes from V4.0.1 + + + The function vTaskList() now suspends the scheduler rather than disabling + interrupts during the creation of the task list. + + Allow a task to delete itself by passing in its own handle. Previously + this could only be done by passing in NULL. + + The tick hook function is now called only within a tick isr. Previously + it was also called when the tick function was called during the scheduler + unlocking process. + +Changes from V4.0.3 + + + Extra checks have been placed in vTaskPrioritySet() to avoid unnecessary + yields. + +Changed from V4.0.4 + + + Bug fix: The 'value' of the event list item is updated when the priority + of a task is changed. Previously only the priority of the TCB itself was + changed. + + When resuming a task a check is first made to see if the task is actually + suspended. + + vTaskPrioritySet() and vTaskResume() no longer use the event list item. + This has not been necessary since V4.0.1 when the xMissedYield handling + was added. + + Implement xTaskResumeFromISR(). + +Changes from V4.0.5 + + + Added utility functions and xOverflowCount variable to facilitate the + queue.c changes. + +Changes from V4.1.2 + + + Tasks that block on events with a timeout of portMAX_DELAY are now + blocked indefinitely if configINCLUDE_vTaskSuspend is defined. + Previously portMAX_DELAY was just the longest block time possible. + +Changes from V4.1.3 + + + Very small change made to xTaskCheckForTimeout() as a result of the + SafeRTOS testing. This corrects the case where the function can return an + invalid value - but only in an extremely unlikely scenario. + +Changes since V4.3.1: + + + Added xTaskGetSchedulerState() function. + + Added prvIsTaskSuspended() to take into account the Occurrence of + vTaskResume() or vTaskResumeFromISR() being called passing in the + handle of a task that appears in the Suspended list only because it + is blocked on an event without a timeout being specified. + + Updated xTaskCheckForTimeout() to take into account that tasks blocked + using the Suspended list should never time out. +*/ + +#include +#include +#include + +#include "FreeRTOS.h" +#include "task.h" + +/* + * Macro to define the amount of stack available to the idle task. + */ +#define tskIDLE_STACK_SIZE configMINIMAL_STACK_SIZE + + +/* + * Default a definitions for backwards compatibility with old + * portmacro.h files. + */ +#ifndef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 16 +#endif + +#ifndef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 0 +#endif + +#ifndef configIDLE_SHOULD_YIELD + #define configIDLE_SHOULD_YIELD 1 +#endif + +#if configMAX_TASK_NAME_LEN < 1 + #undef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 1 +#endif + +#ifndef INCLUDE_xTaskResumeFromISR + #define INCLUDE_xTaskResumeFromISR 1 +#endif + +#ifndef INCLUDE_xTaskGetSchedulerState + #define INCLUDE_xTaskGetSchedulerState 0 +#endif + +/* + * Task control block. A task control block (TCB) is allocated to each task, + * and stores the context of the task. + */ +typedef struct tskTaskControlBlock +{ + volatile portSTACK_TYPE *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */ + xListItem xGenericListItem; /*< List item used to place the TCB in ready and blocked queues. */ + xListItem xEventListItem; /*< List item used to place the TCB in event lists. */ + unsigned portBASE_TYPE uxPriority; /*< The priority of the task where 0 is the lowest priority. */ + portSTACK_TYPE *pxStack; /*< Points to the start of the stack. */ + unsigned portBASE_TYPE uxTCBNumber; /*< This is used for tracing the scheduler and making debugging easier only. */ + signed portCHAR pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ + unsigned portSHORT usStackDepth; /*< Total depth of the stack (when empty). This is defined as the number of variables the stack can hold, not the number of bytes. */ +} tskTCB; + +/*lint -e956 */ + +tskTCB * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. --------------------*/ + +static xList pxReadyTasksLists[ configMAX_PRIORITIES ]; /*< Prioritised ready tasks. */ +static xList xDelayedTaskList1; /*< Delayed tasks. */ +static xList xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +static xList * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ +static xList * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +static xList xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready queue when the scheduler is resumed. */ + +#if ( INCLUDE_vTaskDelete == 1 ) + + static volatile xList xTasksWaitingTermination; /*< Tasks that have been deleted - but the their memory not yet freed. */ + static volatile unsigned portBASE_TYPE uxTasksDeleted = ( unsigned portBASE_TYPE ) 0; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + static xList xSuspendedTaskList; /*< Tasks that are currently suspended. */ + +#endif + +/* File private variables. --------------------------------*/ +static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks = ( unsigned portBASE_TYPE ) 0; +static volatile portTickType xTickCount = ( portTickType ) 0; +static unsigned portBASE_TYPE uxTopUsedPriority = tskIDLE_PRIORITY; +static volatile unsigned portBASE_TYPE uxTopReadyPriority = tskIDLE_PRIORITY; +static volatile signed portBASE_TYPE xSchedulerRunning = pdFALSE; +static volatile unsigned portBASE_TYPE uxSchedulerSuspended = ( unsigned portBASE_TYPE ) pdFALSE; +static volatile unsigned portBASE_TYPE uxMissedTicks = ( unsigned portBASE_TYPE ) 0; +static volatile portBASE_TYPE xMissedYield = ( portBASE_TYPE ) pdFALSE; +static volatile portBASE_TYPE xNumOfOverflows = ( portBASE_TYPE ) 0; +/* Debugging and trace facilities private variables and macros. ------------*/ + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5 ) + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskBLOCKED_CHAR ( ( signed portCHAR ) 'B' ) +#define tskREADY_CHAR ( ( signed portCHAR ) 'R' ) +#define tskDELETED_CHAR ( ( signed portCHAR ) 'D' ) +#define tskSUSPENDED_CHAR ( ( signed portCHAR ) 'S' ) + +/* + * Macros and private variables used by the trace facility. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + #define tskSIZE_OF_EACH_TRACE_LINE ( ( unsigned portLONG ) ( sizeof( unsigned portLONG ) + sizeof( unsigned portLONG ) ) ) + static volatile signed portCHAR * volatile pcTraceBuffer; + static signed portCHAR *pcTraceBufferStart; + static signed portCHAR *pcTraceBufferEnd; + static signed portBASE_TYPE xTracing = pdFALSE; + +#endif + +/* + * Macro that writes a trace of scheduler activity to a buffer. This trace + * shows which task is running when and is very useful as a debugging tool. + * As this macro is called each context switch it is a good idea to undefine + * it if not using the facility. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + #define vWriteTraceToBuffer() \ + { \ + if( xTracing ) \ + { \ + static unsigned portBASE_TYPE uxPreviousTask = 255; \ + \ + if( uxPreviousTask != pxCurrentTCB->uxTCBNumber ) \ + { \ + if( ( pcTraceBuffer + tskSIZE_OF_EACH_TRACE_LINE ) < pcTraceBufferEnd ) \ + { \ + uxPreviousTask = pxCurrentTCB->uxTCBNumber; \ + *( unsigned portLONG * ) pcTraceBuffer = ( unsigned portLONG ) xTickCount; \ + pcTraceBuffer += sizeof( unsigned portLONG ); \ + *( unsigned portLONG * ) pcTraceBuffer = ( unsigned portLONG ) uxPreviousTask; \ + pcTraceBuffer += sizeof( unsigned portLONG ); \ + } \ + else \ + { \ + xTracing = pdFALSE; \ + } \ + } \ + } \ + } + +#else + + #define vWriteTraceToBuffer() + +#endif + + +/* + * Place the task represented by pxTCB into the appropriate ready queue for + * the task. It is inserted at the end of the list. One quirk of this is + * that if the task being inserted is at the same priority as the currently + * executing task, then it will only be rescheduled after the currently + * executing task has been rescheduled. + */ +#define prvAddTaskToReadyQueue( pxTCB ) \ +{ \ + if( pxTCB->uxPriority > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = pxTCB->uxPriority; \ + } \ + vListInsertEnd( ( xList * ) &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ); \ +} + +/* + * Macro that looks at the list of tasks that are currently delayed to see if + * any require waking. + * + * Tasks are stored in the queue in the order of their wake time - meaning + * once one tasks has been found whose timer has not expired we need not look + * any further down the list. + */ +#define prvCheckDelayedTasks() \ +{ \ +register tskTCB *pxTCB; \ + \ + while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ) ) != NULL ) \ + { \ + if( xTickCount < listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ) ) \ + { \ + break; \ + } \ + vListRemove( &( pxTCB->xGenericListItem ) ); \ + /* Is the task waiting on an event also? */ \ + if( pxTCB->xEventListItem.pvContainer ) \ + { \ + vListRemove( &( pxTCB->xEventListItem ) ); \ + } \ + prvAddTaskToReadyQueue( pxTCB ); \ + } \ +} + +/* + * Several functions take an xTaskHandle parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( pxHandle == NULL ) ? ( tskTCB * ) pxCurrentTCB : ( tskTCB * ) pxHandle ) + + +/* File private functions. --------------------------------*/ + +/* + * Utility to ready a TCB for a given task. Mainly just copies the parameters + * into the TCB structure. + */ +static void prvInitialiseTCBVariables( tskTCB *pxTCB, unsigned portSHORT usStackDepth, const signed portCHAR * const pcName, unsigned portBASE_TYPE uxPriority ); + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ); + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( ( INCLUDE_vTaskDelete == 1 ) || ( INCLUDE_vTaskCleanUpResources == 1 ) ) + static void prvDeleteTCB( tskTCB *pxTCB ); +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ); + +/* + * Allocates memory from the heap for a TCB and associated stack. Checks the + * allocation was successful. + */ +static tskTCB *prvAllocateTCBAndStack( unsigned portSHORT usStackDepth ); + +/* + * Called from vTaskList. vListTasks details all the tasks currently under + * control of the scheduler. The tasks may be in one of a number of lists. + * prvListTaskWithinSingleList accepts a list and details the tasks from + * within just that list. + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvListTaskWithinSingleList( signed portCHAR *pcWriteBuffer, xList *pxList, signed portCHAR cStatus ); + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + unsigned portSHORT usTaskCheckFreeStackSpace( const unsigned portCHAR *pucStackByte ); + +#endif + +/* + * Checks that a task being resumed (unsuspended) is actually in the Suspended + * state. + */ +#if ( INCLUDE_vTaskSuspend == 1 ) + + static portBASE_TYPE prvIsTaskSuspended( const tskTCB * const pxTCB ); + +#endif + +/*lint +e956 */ + + + + + +/*----------------------------------------------------------- + * TASK CREATION API documented in task.h + *----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask ) +{ +signed portBASE_TYPE xReturn; +tskTCB * pxNewTCB; +static unsigned portBASE_TYPE uxTaskNumber = 0; /*lint !e956 Static is deliberate - this is guarded before use. */ + + /* Allocate the memory required by the TCB and stack for the new task. + checking that the allocation was successful. */ + pxNewTCB = prvAllocateTCBAndStack( usStackDepth ); + + if( pxNewTCB != NULL ) + { + portSTACK_TYPE *pxTopOfStack; + + /* Setup the newly allocated TCB with the initial state of the task. */ + prvInitialiseTCBVariables( pxNewTCB, usStackDepth, pcName, uxPriority ); + + /* Calculate the top of stack address. This depends on whether the + stack grows from high memory to low (as per the 80x86) or visa versa. + portSTACK_GROWTH is used to make the result positive or negative as + required by the port. */ + #if portSTACK_GROWTH < 0 + { + pxTopOfStack = pxNewTCB->pxStack + ( pxNewTCB->usStackDepth - 1 ); + } + #else + { + pxTopOfStack = pxNewTCB->pxStack; + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pvTaskCode, pvParameters ); + + /* We are going to manipulate the task queues to add this task to a + ready list, so must make sure no interrupts occur. */ + portENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + if( uxCurrentNumberOfTasks == ( unsigned portBASE_TYPE ) 1 ) + { + /* As this is the first task it must also be the current task. */ + pxCurrentTCB = pxNewTCB; + + /* This is the first task to be created so do the preliminary + initialisation required. We will not recover if this call + fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + } + } + + /* Remember the top priority to make context switching faster. Use + the priority in pxNewTCB as this has been capped to a valid value. */ + if( pxNewTCB->uxPriority > uxTopUsedPriority ) + { + uxTopUsedPriority = pxNewTCB->uxPriority; + } + + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + uxTaskNumber++; + + prvAddTaskToReadyQueue( pxNewTCB ); + + xReturn = pdPASS; + } + portEXIT_CRITICAL(); + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + if( xReturn == pdPASS ) + { + if( ( void * ) pxCreatedTask != NULL ) + { + /* Pass the TCB out - in an anonymous way. The calling function/ + task can use this as a handle to delete the task later if + required.*/ + *pxCreatedTask = ( xTaskHandle ) pxNewTCB; + } + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + then it should run now. */ + if( pxCurrentTCB->uxPriority < uxPriority ) + { + taskYIELD(); + } + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( xTaskHandle pxTaskToDelete ) + { + tskTCB *pxTCB; + + taskENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + deleted. */ + if( pxTaskToDelete == pxCurrentTCB ) + { + pxTaskToDelete = NULL; + } + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToDelete ); + + /* Remove task from the ready list and place in the termination list. + This will stop the task from be scheduled. The idle task will check + the termination list and free up any memory allocated by the + scheduler for the TCB and stack. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + } + taskEXIT_CRITICAL(); + + /* Force a reschedule if we have just deleted the current task. */ + if( xSchedulerRunning != pdFALSE ) + { + if( ( void * ) pxTaskToDelete == NULL ) + { + taskYIELD(); + } + } + } + +#endif + + + + + + +/*----------------------------------------------------------- + * TASK CONTROL API documented in task.h + *----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelayUntil == 1 ) + + void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement ) + { + portTickType xTimeToWake; + portBASE_TYPE xAlreadyYielded, xShouldDelay = pdFALSE; + + vTaskSuspendAll(); + { + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xTickCount ) ) + { + xShouldDelay = pdTRUE; + } + } + else + { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xTickCount ) ) + { + xShouldDelay = pdTRUE; + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay ) + { + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( !xAlreadyYielded ) + { + taskYIELD(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( portTickType xTicksToDelay ) + { + portTickType xTimeToWake; + signed portBASE_TYPE xAlreadyYielded = pdFALSE; + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( portTickType ) 0 ) + { + vTaskSuspendAll(); + { + /* A task that is removed from the event list while the + scheduler is suspended will not get placed in the ready + list or removed from the blocked list until the scheduler + is resumed. + + This task cannot be in an event list as it is the currently + executing task. */ + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + xAlreadyYielded = xTaskResumeAll(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( !xAlreadyYielded ) + { + taskYIELD(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ) + { + tskTCB *pxTCB; + unsigned portBASE_TYPE uxReturn; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( pxTask ); + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL(); + + return uxReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ) + { + tskTCB *pxTCB; + unsigned portBASE_TYPE uxCurrentPriority, xYieldRequired = pdFALSE; + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= configMAX_PRIORITIES ) + { + uxNewPriority = configMAX_PRIORITIES - 1; + } + + taskENTER_CRITICAL(); + { + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( pxTask ); + uxCurrentPriority = pxTCB->uxPriority; + + if( uxCurrentPriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + priority than the calling task. */ + if( uxNewPriority > pxCurrentTCB->uxPriority ) + { + if( pxTask != NULL ) + { + /* The priority of another task is being raised. If we + were raising the priority of the currently running task + there would be no need to switch as it must have already + been the highest priority task. */ + xYieldRequired = pdTRUE; + } + } + else if( pxTask == NULL ) + { + /* Setting our own priority down means there may now be another + task of higher priority that is ready to execute. */ + xYieldRequired = pdTRUE; + } + + pxTCB->uxPriority = uxNewPriority; + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxNewPriority ); + + /* If the task is in the blocked or suspended list we need do + nothing more than change it's priority variable. However, if + the task is in a ready list it needs to be removed and placed + in the queue appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxCurrentPriority ] ), &( pxTCB->xGenericListItem ) ) ) + { + /* The task is currently in its ready list - remove before adding + it to it's new ready list. As we are in a critical section we + can do this even if the scheduler is suspended. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + } + + if( xYieldRequired == pdTRUE ) + { + taskYIELD(); + } + } + } + taskEXIT_CRITICAL(); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( xTaskHandle pxTaskToSuspend ) + { + tskTCB *pxTCB; + + taskENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + suspended. */ + if( pxTaskToSuspend == pxCurrentTCB ) + { + pxTaskToSuspend = NULL; + } + + /* If null is passed in here then we are suspending ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToSuspend ); + + /* Remove task from the ready/delayed list and place in the suspended list. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + } + taskEXIT_CRITICAL(); + + /* We may have just suspended the current task. */ + if( ( void * ) pxTaskToSuspend == NULL ) + { + taskYIELD(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + static portBASE_TYPE prvIsTaskSuspended( const tskTCB * const pxTCB ) + { + portBASE_TYPE xReturn = pdFALSE; + + /* Is the task we are attempting to resume actually in the + suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) != pdTRUE ) + { + /* Is it in the suspended list because it is in the + Suspended state? It is possible to be in the suspended + list because it is blocked on a task with no timeout + specified. */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) == pdTRUE ) + { + xReturn = pdTRUE; + } + } + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( xTaskHandle pxTaskToResume ) + { + tskTCB *pxTCB; + + /* Remove the task from whichever list it is currently in, and place + it in the ready list. */ + pxTCB = ( tskTCB * ) pxTaskToResume; + + /* The parameter cannot be NULL as it is impossible to resume the + currently executing task. */ + if( pxTCB != NULL ) + { + taskENTER_CRITICAL(); + { + if( prvIsTaskSuspended( pxTCB ) == pdTRUE ) + { + /* As we are in a critical section we can access the ready + lists even if the scheduler is suspended. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + + /* We may have just resumed a higher priority task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, but + will leave the lists in the correct state for the next yield. */ + taskYIELD(); + } + } + } + taskEXIT_CRITICAL(); + } + } + +#endif + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume ) + { + portBASE_TYPE xYieldRequired = pdFALSE; + tskTCB *pxTCB; + + pxTCB = ( tskTCB * ) pxTaskToResume; + + if( prvIsTaskSuspended( pxTCB ) == pdTRUE ) + { + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + xYieldRequired = ( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ); + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + } + else + { + /* We cannot access the delayed or ready lists, so will hold this + task pending until the scheduler is resumed, at which point a + yield will be preformed if necessary. */ + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + + return xYieldRequired; + } + +#endif + + + + +/*----------------------------------------------------------- + * PUBLIC SCHEDULER CONTROL documented in task.h + *----------------------------------------------------------*/ + + +void vTaskStartScheduler( void ) +{ +portBASE_TYPE xReturn; + + /* Add the idle task at the lowest priority. */ + xReturn = xTaskCreate( prvIdleTask, ( signed portCHAR * ) "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, tskIDLE_PRIORITY, ( xTaskHandle * ) NULL ); + + if( xReturn == pdPASS ) + { + /* Interrupts are turned off here, to ensure a tick does not occur + before or during the call to xPortStartScheduler(). The stacks of + the created tasks contain a status word with interrupts switched on + so interrupts will automatically get re-enabled when the first task + starts to run. + + STEPPING THROUGH HERE USING A DEBUGGER CAN CAUSE BIG PROBLEMS IF THE + DEBUGGER ALLOWS INTERRUPTS TO BE PROCESSED. */ + portDISABLE_INTERRUPTS(); + + xSchedulerRunning = pdTRUE; + xTickCount = ( portTickType ) 0; + + /* Setting up the timer tick is hardware specific and thus in the + portable interface. */ + if( xPortStartScheduler() ) + { + /* Should not reach here as if the scheduler is running the + function will not return. */ + } + else + { + /* Should only reach here if a task calls xTaskEndScheduler(). */ + } + } +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + /* Stop the scheduler interrupts and call the portable scheduler end + routine so the original ISRs can be restored if necessary. The port + layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + portENTER_CRITICAL(); + ++uxSchedulerSuspended; + portEXIT_CRITICAL(); +} +/*----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskResumeAll( void ) +{ +register tskTCB *pxTCB; +signed portBASE_TYPE xAlreadyYielded = pdFALSE; + + /* It is possible that an ISR caused a task to be removed from an event + list while the scheduler was suspended. If this was the case then the + removed task will have been added to the xPendingReadyList. Once the + scheduler has been resumed it is safe to move all the pending ready + tasks from this list into their appropriate ready list. */ + portENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + if( uxCurrentNumberOfTasks > ( unsigned portBASE_TYPE ) 0 ) + { + portBASE_TYPE xYieldRequired = pdFALSE; + + /* Move any readied tasks from the pending list into the + appropriate ready list. */ + while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xPendingReadyList ) ) ) != NULL ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + + /* If we have moved a task that has a priority higher than + the current task then we should yield. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + } + + /* If any ticks occurred while the scheduler was suspended then + they should be processed now. This ensures the tick count does not + slip, and that any delayed tasks are resumed at the correct time. */ + if( uxMissedTicks > ( unsigned portBASE_TYPE ) 0 ) + { + while( uxMissedTicks > ( unsigned portBASE_TYPE ) 0 ) + { + vTaskIncrementTick(); + --uxMissedTicks; + } + + /* As we have processed some ticks it is appropriate to yield + to ensure the highest priority task that is ready to run is + the task actually running. */ + xYieldRequired = pdTRUE; + } + + if( ( xYieldRequired == pdTRUE ) || ( xMissedYield == pdTRUE ) ) + { + xAlreadyYielded = pdTRUE; + xMissedYield = pdFALSE; + taskYIELD(); + } + } + } + } + portEXIT_CRITICAL(); + + return xAlreadyYielded; +} + + + + + + +/*----------------------------------------------------------- + * PUBLIC TASK UTILITIES documented in task.h + *----------------------------------------------------------*/ + + + +portTickType xTaskGetTickCount( void ) +{ +portTickType xTicks; + + /* Critical section required if running on a 16 bit processor. */ + taskENTER_CRITICAL(); + { + xTicks = xTickCount; + } + taskEXIT_CRITICAL(); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ) +{ +unsigned portBASE_TYPE uxNumberOfTasks; + + taskENTER_CRITICAL(); + uxNumberOfTasks = uxCurrentNumberOfTasks; + taskEXIT_CRITICAL(); + + return uxNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_vTaskDelete == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + void vTaskList( signed portCHAR *pcWriteBuffer ) + { + unsigned portBASE_TYPE uxQueue; + + /* This is a VERY costly function that should be used for debug only. + It leaves interrupts disabled for a LONG time. */ + + vTaskSuspendAll(); + { + /* Run through all the lists that could potentially contain a TCB and + report the task name, state and stack high water mark. */ + + pcWriteBuffer[ 0 ] = ( signed portCHAR ) 0x00; + strcat( ( portCHAR * ) pcWriteBuffer, ( const portCHAR * ) "\r\n" ); + + uxQueue = uxTopUsedPriority + 1; + + do + { + uxQueue--; + + if( !listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &( pxReadyTasksLists[ uxQueue ] ), tskREADY_CHAR ); + } + }while( uxQueue > ( unsigned portSHORT ) tskIDLE_PRIORITY ); + + if( !listLIST_IS_EMPTY( pxDelayedTaskList ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) pxDelayedTaskList, tskBLOCKED_CHAR ); + } + + if( !listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) pxOverflowDelayedTaskList, tskBLOCKED_CHAR ); + } + + if( !listLIST_IS_EMPTY( &xTasksWaitingTermination ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &xTasksWaitingTermination, tskDELETED_CHAR ); + } + + if( !listLIST_IS_EMPTY( &xSuspendedTaskList ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &xSuspendedTaskList, tskSUSPENDED_CHAR ); + } + } + xTaskResumeAll(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskStartTrace( signed portCHAR * pcBuffer, unsigned portLONG ulBufferSize ) + { + portENTER_CRITICAL(); + { + pcTraceBuffer = ( volatile signed portCHAR * volatile )pcBuffer; + pcTraceBufferStart = pcBuffer; + pcTraceBufferEnd = pcBuffer + ( ulBufferSize - tskSIZE_OF_EACH_TRACE_LINE ); + xTracing = pdTRUE; + } + portEXIT_CRITICAL(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + unsigned portLONG ulTaskEndTrace( void ) + { + unsigned portLONG ulBufferLength; + + portENTER_CRITICAL(); + xTracing = pdFALSE; + portEXIT_CRITICAL(); + + ulBufferLength = ( unsigned portLONG ) ( pcTraceBuffer - pcTraceBufferStart ); + + return ulBufferLength; + } + +#endif + + + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + * documented in task.h + *----------------------------------------------------------*/ + + +inline void vTaskIncrementTick( void ) +{ + /* Called by the portable layer each time a tick interrupt occurs. + Increments the tick then checks to see if the new tick value will cause any + tasks to be unblocked. */ + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + ++xTickCount; + if( xTickCount == ( portTickType ) 0 ) + { + xList *pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. + If there are any items in pxDelayedTaskList here then there is + an error! */ + pxTemp = pxDelayedTaskList; + pxDelayedTaskList = pxOverflowDelayedTaskList; + pxOverflowDelayedTaskList = pxTemp; + xNumOfOverflows++; + } + + /* See if this tick has made a timeout expire. */ + prvCheckDelayedTasks(); + } + else + { + ++uxMissedTicks; + + /* The tick hook gets called at regular intervals, even if the + scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + extern void vApplicationTickHook( void ); + + vApplicationTickHook(); + } + #endif + } + + #if ( configUSE_TICK_HOOK == 1 ) + { + extern void vApplicationTickHook( void ); + + /* Guard against the tick hook being called when the missed tick + count is being unwound (when the scheduler is being unlocked. */ + if( uxMissedTicks == 0 ) + { + vApplicationTickHook(); + } + } + #endif +} +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_vTaskCleanUpResources == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + void vTaskCleanUpResources( void ) + { + unsigned portSHORT usQueue; + volatile tskTCB *pxTCB; + + usQueue = ( unsigned portSHORT ) uxTopUsedPriority + ( unsigned portSHORT ) 1; + + /* Remove any TCB's from the ready queues. */ + do + { + usQueue--; + + while( !listLIST_IS_EMPTY( &( pxReadyTasksLists[ usQueue ] ) ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &( pxReadyTasksLists[ usQueue ] ) ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + }while( usQueue > ( unsigned portSHORT ) tskIDLE_PRIORITY ); + + /* Remove any TCB's from the delayed queue. */ + while( !listLIST_IS_EMPTY( &xDelayedTaskList1 ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &xDelayedTaskList1 ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + + /* Remove any TCB's from the overflow delayed queue. */ + while( !listLIST_IS_EMPTY( &xDelayedTaskList2 ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &xDelayedTaskList2 ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + + while( !listLIST_IS_EMPTY( &xSuspendedTaskList ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &xSuspendedTaskList ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + } + +#endif +/*-----------------------------------------------------------*/ + +void vTaskSwitchContext( void ) +{ + if( uxSchedulerSuspended != ( unsigned portBASE_TYPE ) pdFALSE ) + { + /* The scheduler is currently suspended - do not allow a context + switch. */ + xMissedYield = pdTRUE; + return; + } + + /* Find the highest priority queue that contains ready tasks. */ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) ) + { + --uxTopReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the tasks of the + same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); + vWriteTraceToBuffer(); +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( xList *pxEventList, portTickType xTicksToWait ) +{ +portTickType xTimeToWake; + + /* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED. */ + + /* Place the event list item of the TCB in the appropriate event list. + This is placed in the list in priority order so the highest priority task + is the first to be woken by the event. */ + vListInsert( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) ); + + /* We must remove ourselves from the ready list before adding ourselves + to the blocked list as the same list item is used for both lists. We have + exclusive access to the ready lists as the scheduler is locked. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( xTicksToWait == portMAX_DELAY ) + { + /* Add ourselves to the suspended task list instead of a delayed task + list to ensure we are not woken by a timing event. We will block + indefinitely. */ + vListInsertEnd( ( xList * ) &xSuspendedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + } + #else + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + #endif +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskRemoveFromEventList( const xList *pxEventList ) +{ +tskTCB *pxUnblockedTCB; +portBASE_TYPE xReturn; + + /* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED. It can also be called from within an ISR. */ + + /* The event list is sorted in priority order, so we can remove the + first in the list, remove the TCB from the delayed list, and add + it to the ready list. + + If an event is for a queue that is locked then this function will never + get called - the lock count on the queue will get modified instead. This + means we can always expect exclusive access to the event list here. */ + pxUnblockedTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + vListRemove( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + vListRemove( &( pxUnblockedTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxUnblockedTCB ); + } + else + { + /* We cannot access the delayed or ready lists, so will hold this + task pending until the scheduler is resumed. */ + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has + a higher priority than the calling task. This allows + the calling task to know if it should force a context + switch now. */ + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( xTimeOutType *pxTimeOut ) +{ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType *pxTimeOut, portTickType * const pxTicksToWait ) +{ +portBASE_TYPE xReturn; + + #if ( INCLUDE_vTaskSuspend == 1 ) + /* If INCLUDE_vTaskSuspend is set to 1 and the block time specified is + the maximum block time then the task should block indefinitely, and + therefore never time out. */ + if( *pxTicksToWait == portMAX_DELAY ) + { + xReturn = pdFALSE; + } + else /* We are not blocking indefinitely, perform the checks below. */ + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xTickCount >= pxTimeOut->xTimeOnEntering ) ) + { + /* The tick count is greater than the time at which vTaskSetTimeout() + was called, but has also overflowed since vTaskSetTimeOut() was called. + It must have wrapped all the way around and gone past us again. This + passed since vTaskSetTimeout() was called. */ + xReturn = pdTRUE; + } + else if( ( xTickCount - pxTimeOut->xTimeOnEntering ) < *pxTicksToWait ) + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= ( xTickCount - pxTimeOut->xTimeOnEntering ); + vTaskSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + xMissedYield = pdTRUE; +} + +/* + * ----------------------------------------------------------- + * The Idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* See if any tasks have been deleted. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + see if any other task has become available. If we are using + preemption we don't need to do this as any task becoming available + will automatically get the processor anyway. */ + taskYIELD(); + } + #endif + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + timesliced. If a task that is sharing the idle priority is ready + to run then the idle task should yield before the end of the + timeslice. + + A critical region is not required here as we are just reading from + the list, and an occasional incorrect value will not matter. If + the ready list at the idle priority contains more than one task + then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( unsigned portBASE_TYPE ) 1 ) + { + taskYIELD(); + } + } + #endif + + #if ( configUSE_IDLE_HOOK == 1 ) + { + extern void vApplicationIdleHook( void ); + + /* Call the user defined function from within the idle task. This + allows the application designer to add background functionality + without the overhead of a separate task. + NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationIdleHook(); + } + #endif + } +} /*lint !e715 pvParameters is not accessed but all task functions require the same prototype. */ + + + + + + + +/*----------------------------------------------------------- + * File private functions documented at the top of the file. + *----------------------------------------------------------*/ + + + +static void prvInitialiseTCBVariables( tskTCB *pxTCB, unsigned portSHORT usStackDepth, const signed portCHAR * const pcName, unsigned portBASE_TYPE uxPriority ) +{ + pxTCB->usStackDepth = usStackDepth; + + /* Store the function name in the TCB. */ + strncpy( ( char * ) pxTCB->pcTaskName, ( const char * ) pcName, ( unsigned portSHORT ) configMAX_TASK_NAME_LEN ); + pxTCB->pcTaskName[ ( unsigned portSHORT ) configMAX_TASK_NAME_LEN - ( unsigned portSHORT ) 1 ] = '\0'; + + /* This is used as an array index so must ensure it's not too large. */ + if( uxPriority >= configMAX_PRIORITIES ) + { + uxPriority = configMAX_PRIORITIES - 1; + } + + pxTCB->uxPriority = uxPriority; + + vListInitialiseItem( &( pxTCB->xGenericListItem ) ); + vListInitialiseItem( &( pxTCB->xEventListItem ) ); + + /* Set the pxTCB as a link back from the xListItem. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority ); + listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseTaskLists( void ) +{ +unsigned portBASE_TYPE uxPriority; + + for( uxPriority = 0; uxPriority < configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( xList * ) &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( ( xList * ) &xDelayedTaskList1 ); + vListInitialise( ( xList * ) &xDelayedTaskList2 ); + vListInitialise( ( xList * ) &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( ( xList * ) &xTasksWaitingTermination ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( ( xList * ) &xSuspendedTaskList ); + } + #endif + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + #if ( INCLUDE_vTaskDelete == 1 ) + { + portBASE_TYPE xListIsEmpty; + + /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called + too often in the idle task. */ + if( uxTasksDeleted > ( unsigned portBASE_TYPE ) 0 ) + { + vTaskSuspendAll(); + xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); + xTaskResumeAll(); + + if( !xListIsEmpty ) + { + tskTCB *pxTCB; + + portENTER_CRITICAL(); + { + pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xTasksWaitingTermination ) ); + vListRemove( &( pxTCB->xGenericListItem ) ); + --uxCurrentNumberOfTasks; + --uxTasksDeleted; + } + portEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + } + } + #endif +} +/*-----------------------------------------------------------*/ + +static tskTCB *prvAllocateTCBAndStack( unsigned portSHORT usStackDepth ) +{ +tskTCB *pxNewTCB; + + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function. */ + pxNewTCB = ( tskTCB * ) pvPortMalloc( sizeof( tskTCB ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( portSTACK_TYPE * ) pvPortMalloc( ( ( size_t )usStackDepth ) * sizeof( portSTACK_TYPE ) ); + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + else + { + /* Just to help debugging. */ + memset( pxNewTCB->pxStack, tskSTACK_FILL_BYTE, usStackDepth * sizeof( portSTACK_TYPE ) ); + } + } + + return pxNewTCB; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvListTaskWithinSingleList( signed portCHAR *pcWriteBuffer, xList *pxList, signed portCHAR cStatus ) + { + volatile tskTCB *pxNextTCB, *pxFirstTCB; + static portCHAR pcStatusString[ 50 ]; + unsigned portSHORT usStackRemaining; + + /* Write the details of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + usStackRemaining = usTaskCheckFreeStackSpace( ( unsigned portCHAR * ) pxNextTCB->pxStack ); + sprintf( pcStatusString, ( portCHAR * ) "%s\t\t%c\t%u\t%u\t%u\r\n", pxNextTCB->pcTaskName, cStatus, ( unsigned int ) pxNextTCB->uxPriority, usStackRemaining, ( unsigned int ) pxNextTCB->uxTCBNumber ); + strcat( ( portCHAR * ) pcWriteBuffer, ( portCHAR * ) pcStatusString ); + + } while( pxNextTCB != pxFirstTCB ); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + unsigned portSHORT usTaskCheckFreeStackSpace( const unsigned portCHAR *pucStackByte ) + { + register unsigned portSHORT usCount = 0; + + while( *pucStackByte == tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + usCount++; + } + + usCount /= sizeof( portSTACK_TYPE ); + + return usCount; + } +#endif +/*-----------------------------------------------------------*/ + + + +#if ( ( INCLUDE_vTaskDelete == 1 ) || ( INCLUDE_vTaskCleanUpResources == 1 ) ) + + static void prvDeleteTCB( tskTCB *pxTCB ) + { + /* Free up the memory allocated by the scheduler for the task. It is up to + the task to free any memory allocated at the application level. */ + vPortFree( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + +#endif + + +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) + + xTaskHandle xTaskGetCurrentTaskHandle( void ) + { + xTaskHandle xReturn; + + portENTER_CRITICAL(); + { + xReturn = ( xTaskHandle ) pxCurrentTCB; + } + portEXIT_CRITICAL(); + + return xReturn; + } + +#endif + +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetSchedulerState == 1 ) + + portBASE_TYPE xTaskGetSchedulerState( void ) + { + portBASE_TYPE xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } + +#endif + + diff --git a/FreeRTOSConfig.h b/FreeRTOSConfig.h new file mode 100644 index 0000000..30c5bfa --- /dev/null +++ b/FreeRTOSConfig.h @@ -0,0 +1,76 @@ +/* + FreeRTOS.org V4.3.1 - Copyright (C) 2003-2007 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + FreeRTOS.org is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FreeRTOS.org; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes FreeRTOS.org, without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details of how and when the exception + can be applied. + + *************************************************************************** + See http://www.FreeRTOS.org for documentation, latest information, license + and contact details. Please ensure to read the configuration and relevant + port sections of the online documentation. + + Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along + with commercial development and support options. + *************************************************************************** +*/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +#include + +// +// Application specific definitions. +// +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ ((unsigned portLONG) 48000000) // =12.0000MHz xtal multiplied by 4 using the PLL. +#define configTICK_RATE_HZ ((portTickType) 100) +#define configMAX_PRIORITIES ((unsigned portBASE_TYPE) 4) +#define configMINIMAL_STACK_SIZE ((unsigned portSHORT) 128) +#define configTOTAL_HEAP_SIZE ((size_t) (18 * 1024)) +#define configMAX_TASK_NAME_LEN (8) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 + +// +// Co-routine definitions +// +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES (2) + +// +// Set the following definitions to 1 to include the API function, or zero +// to exclude the API function. +// +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_vPortUsedMem 1 + +#endif /* FREERTOS_CONFIG_H */ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..28b15e1 --- /dev/null +++ b/Makefile @@ -0,0 +1,81 @@ +.SILENT: + +# +# -D CFG_CONSOLE_USB for console on USB +# -D CFG_CONSOLE_UART0 for console on UART0 +# -D CFG_CONSOLE_UART1 for console on UART1 instead of USB (disables GPS task, baud rate set to 115200) +# -D CFG_USB_MSC to use SD/MMC as a mass storage class device over USB +# +export LPC2148DEMO_OPTS=-D CFG_CONSOLE_USB + +# +# These shouldn't need to be changed +# +export CC=arm-elf-gcc +export AR=arm-elf-ar +export OBJCOPY=arm-elf-objcopy +export OBJDUMP=arm-elf-objdump +export CRT0=boot.s +export WARNINGS=-Wall -Wextra -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wsign-compare -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused +export CFLAGS=$(WARNINGS) -D RUN_MODE=RUN_FROM_ROM -D GCC_ARM7 $(INCLUDES) $(BASEINCLUDE) -mcpu=arm7tdmi -T$(LDSCRIPT) -g -O3 -fomit-frame-pointer $(LPC2148DEMO_OPTS) +export LDSCRIPT=lpc2148-rom.ld +export LINKER_FLAGS=$(COMMON)/common.a -Xlinker -olpc2148.elf -Xlinker -M -Xlinker -Map=lpc2148.map +export ROOT=$(shell pwd) +export BASEINCLUDE=-I$(ROOT) -I$(ROOT)/FreeRTOS/include +export COMMON=$(ROOT)/common + +# +# Project sub-directories +# +SUBDIRS=FreeRTOS adc cpu dac eints fatfs fiq gps i2c iap leds monitor newlib rtc sensors swi uart usb usbmass usbser + +SRC_FILES = main.c + +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all : + @for i in $(SUBDIRS); do \ + (cd $$i; $(MAKE) $(MFLAGS) $(MYMAKEFLAGS) all); done + make lpc2148.hex + +lpc2148.hex : .depend Makefile lpc2148.elf + $(OBJCOPY) lpc2148.elf -O ihex lpc2148.hex + @echo "Length is " `grep __"end_of_text__ = ." *.map | cut -b 17-35` "bytes" + +lpc2148.elf : .depend Makefile $(ARM_OBJ) $(COMMON)/common.a $(CRT0) $(LDSCRIPT) + $(CC) $(CFLAGS) $(ARM_OBJ) -nostartfiles $(CRT0) $(LINKER_FLAGS) + $(OBJDUMP) -d -S lpc2148.elf >lpc2148.lst + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +# +# Utility targets +# +.PHONY: tags +tags : + @rm -f ctags + find . -name \*.c -exec ctags -a {} \; + find . -name \*.h -exec ctags -a {} \; + +.PHONEY: clean +clean : + find . -name \*.o -exec rm -f {} \; + find . -name .depend -exec rm -f {} \; + rm -f *.map *.lst *.elf *.hex .depend $(COMMON)/common.a + +# +# +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/README b/README new file mode 100644 index 0000000..1a34a80 --- /dev/null +++ b/README @@ -0,0 +1,741 @@ + J.C. Wren + 2007/07/22 + jcwren@jcwren.com + + The most recent version of this package may be found at http://jcwren.com/arm + + I'm changing the format of how updates are documented. Rather than including + it in the body of the text, they will be appended to the front, with a date. + + 2007/07/22, version 1.20: + + Added interrupt driven I2C master transmit and receive code. I'm pretty + confident that all the cases are handled correctly, but I don't have any + way to introduce certain errors for testing. There are now two files in + the ./i2c directory, "i2cInt.c" and "i2cPolled.c". Edit the ./i2c/Makefile + to select interrupt drive or polled I2C handling. There is some support + for debugging I2C the interrupt drive routines. As interrupts occur, the + various state changes are recorded, and the 'i2c dump' command will display + them. This is disabled by default, but can be enabled by editing the + ./i2c/i2cInt.c file and defining I2C_DEBUG. + + Added raw I2C support. The 'i2c' commands allow directly reading and + writing an I2C device (as opposed to using the LM75 wrapper for LM75's, or + the EEPROM wrapper for EEPROMs). See 'i2c help' for a list of commands. + Note that when reading from an I2C device via the raw commands, a maximum + of 16 bytes may be read or written. If you need more, change the buffer + sizes in the appropriate commands in ./monitor/monitor.c, and also the + maximum number of arguments in the command dispatch table (commandListI2C). + + Added EEPROM support. The 'ee' subset of commands allow reading and + writing of a 24Cxxx series type EEPROM. The code is currently targeted at + an Atmel 24C1024 128x8 part (I had some laying around). It should be + pretty easy to rework them for any smaller part. See 'ee help' for a list + of supported commands. + + Fixed the LM75 support to allow writing the config, TOS and THYST + registers. The previous versions allowed reading the registers, but I + forgot to write code to allow changing them. Oops. + + Moved 'date' and 'setdate' commands into the 'rtc' sub-menu as 'get' and + 'set', respectively. Added 'alarm' command that allows setting an alarm + date/time, or disabling it. When an alarm fires, 'ALARM -- YYYY/MM/DD + HH:MM:SS' is printed to the console. Also added 'periodic', which when + enabled, prints 'PERIODIC -- YYYY/MM/DD HH:MM:SS' to the console at the top + of every minute. The RTC also demonstrates using the default vector + address functionality of the non-vectored IRQs in the VIC (alternatively, + by un-defining RTC_NONVECTOREDIRQ in the ./rtc/rtc.h file, a regular + vectored IRQ can be used). + + Note on the RTC alarm and periodic output: It's really gross. Console + input is handled by a libc read() call. This is a blocking call, + implemented by doing a FreeRTOS xQueueReceive in the console device code + (UART 0, UART 1, or USB). So the only way to get the read() to return so + the CCI can output the message is to reserve the special characters 0xfe + and 0xff (something a user can usually never type). When the CCI getline + routine sees either of those characters, it immediately returns. These + characters are then checked for by the CCI command parser, and either an + alarm or periodic message output. If the user is in the process of typing + in a command, the input will be lost. Without totally rewriting the CCI + into a queue based message passing architecture, I couldn't find a more + elegant way to handle this. So basically, the RTC interrupt sends a 0xfe + or 0xff into the input buffer for the console device, which then returns + the character, which is then processed by the CCI code. + + Changed the 'task' command to a 'mem' sub-command. Added the 'map' + command, which shows how memory is allocated. Overview: The .txt section + contains the program code, the initialization values for static data in RAM + (statements like 'static int i = 172;'), and the glue section (I believe + this is where ARM/THUMB inter-networking code is placed). The .data section + is the area of RAM that gets initialized from the constants in FLASH at + startup (so that i == 172). The .bss section is all static data that is + zero length (statements like 'static int foo [12]'). The 'map' command + prints out the starting and ending address of each area (size calulation is + left to the user). Also included is the starting and ending addresses for + the various stacks (undefined, abort, FIQ, IRQ, service), the start of heap, + and the current heap end. The scheduler executes in supervisor mode, tasks + execute in system mode. + + Added FIQ demo. Timer 1 is set up to interrupt at 8hz (8 times a second, + or every 125 milliseconds), and is configured as a fast interrupt. The + interrupt handler does nothing more than increment a counter. The 'fiq on' + command will enable the timer, 'fiq off' will disable it, 'fiq count' will + print the counter value, and 'fiq clear' will reset the counter to 0. As + long as the FIQ is enabled, it should be merrily counting along. NOTE: the + actual FIQ vector is in ./boot.s. Also in this file is the FIQ stack size. + I've set it to 32 bytes (8 words). This was derived empirically by + examining the lpc2148.lst file, and seeing how many registers were pushed + by the fiqISR code. Only 3 registers are pushed, so 8 words was deemed + enough space. An interrupt that actually does anything substantial will + require more stack space, and the FIQ_STACK_SIZE should be adjusted + accordingly. The FIQ in the CPSR is enabled by FreeRTOS when it starts the + scheduler, just like the IRQ. + + The stacks have been tuned down pretty small to allow a larger heap area. + Many boot.s files allocate 1K for the supervisor stack, and another 1K for + the IRQ stack. I've tried to exercise all the functions in the demo to get + a feel for stack usage, and both of those stacks have been tuned down to + 256 bytes each. There shouldn't be any issue with using a lot of stack in + any code prior to the call to vTaskStartScheduler(), since the supervisor + stack will overflow into system/user stack space. Once tasks are running, + they have their own private stack spaces inside the FreeRTOS allocated + memory. If the interrupt routines are modified to use more dynamic space, + then the interrupt stack may need to be increased. So far, I've seen less + than 50% utilization. The FIQ stack is very small, as it does nothing more + than increment a counter. More complex FIQ routines will need more space. + + Fixed problem with CCI 'mv' command failing. Default compiliation options + for newlib for ARM don't define HAVE_RENAME, so the newlib rename() was + trying to do the link/unlink method of rename a file. FatFS (and FAT file + systems) don't support links, so this was always failing, since link() + returns -1. Provided our own rename() in newlib/syscalls.c to override the + newlib rename(). + + Added a data abort, prefetch abort and undefined instruction handler. The + abort handler works by saving the state of the CPU to a block of memory, + then enabling the watchdog to force a reset. This method was used instead + of printing directly to the serial port, as some people are using the USB + as the console port, and there's no gaurantee that the system is still + stable enough for USB to work. So instead, the state is saved, the reset + is forced, and the user can then use the 'abort' set of commands to examine + the system state. The 'regs' command will display the registers at the + time of the abort, and print the opcode of the instruction that failed + (except for prefetch abort). The 'clear' command sets the memory used by + the abort handler to 0's. The 'dirty' command sets the sigil used to + indicate if the abort memory contains valid data. 'dabort', 'pabort' and + 'undef' force each of the types of aborts. To try it, start the system, + type 'abort clear', then 'abort regs'. All registers should be 0. Now + type 'abort dabort'. This forces an access to location 0x40008000, which + does not exist. After the LPC2148 has reset, type 'abort regs'. Examine + the PC value, open the lpc2148.lst file in an editor, and search for that + address. You should find that the abort occurred in the monitorAbortDabort + code, at the 'ldrb' instruction. Note that if the PC is showing somewhere + in the boot.s code area, it's likely a double abort is occuring. Most + likely the stack pointer was already corrupted at the time of the abort, + and when the stack is being copied, it's reading from memory that will + cause a data abort. After an abort, you'll want to do a 'wdt clear' to + clear the WDMOD.WDTOF flag, otherwise you'll be unable to re-enter ISP mode + without a power cycle. + + Added 'misc' menu, which right now consists of the 'sizeof' command. This + displays the size of the common C data types (just in case you weren't sure + a void * is 4 byes). I'll add others here later, like when FreeRTOS starts + exposing structure sizes in a future release. + + Updated FreeRTOS to version 4.4.0 + + CURRENTLY BROKE, WAITING FOR JTAG DONGLE (feel free to submit a fix). + Added USB mass storage capability. If CFG_USB_MSC is defined in the + Makefile, USB serial support will be disabled, and mass storage enabled. + This allows the MMC/SD card to be mounted like a disk drive. DANGER! The + CCI commands for file management remain enabled. This means you can create + a file on the Windows (or Linux) mounted device, and see the changes from + the CCI. HOWEVER: The SPI routines that read/write the MMC/SD card are not + thread-safe, so a USB request to read/write the disk can interrupt a CCI + command. It's crazy dangerous to actually use the CCI file commands while + the MMC/SD card is mounted under Windows or Linux. What you can do is + mount the MMC/SD card, copy files to/from it, unmount it, then use the CCI + commands to see that things really changed. A later revision of the demo + package will likely at least protect the SPI I/O from being interrupted + (although this defeats the 'Real' in RTOS to do so). + + +Overview: + + This package demonstrates using LPCUSB and FatFS under FreeRTOS on the Olimex + LPC2148 board, using GCC and newlib. Examples include FreeRTOS queues and + semaphores, LPC2148 analog to digital converters (ADCs), external interrupts, + the real-time clock (RTC), general purpose IO (GPIO), serial ports (UARTs), + and USB. Also included is a newlib syscalls.c that almost completely + implements all syscalls.c functions. + + The package (as built, .hex file included) presents the USB port as a virtual + comm port. The virtualized port is used to talk to the console command + interpeter (CCI), that allows various functions to be exercised. + Alternatively, the package can re-compiled to use UART0 as the console port. + + If a GPS with NMEA output at 4800 baud is connected to UART1, one of the + tasks will parse the NMEA input stream, and display a position report. In + addition, the RTC may be set from the GPS time/date. + + FatFS support is included, and the CCI has several Unix-y commands to + manipulate files (mkfs, df, ls, mkdir, rmdir, rm, mv, cp, chmod, and sync). + There is also a command that allows through-put testing on the MMC/SD card. + + This package exists because I wanted to familiarize myself with the LPC2148, + FreeRTOS, FatFS and LPCUSB for a personal project, using GCC and newlib (who + can afford those commercial packages? Not I). By slightly modifying the + resulting framework, I was able to produce a package that others may possibly + find useful. + + +Software tools: + + The package compiles using the arm-elf GCC package. Gentoo users can + install this by emerging the 'crossdev' package, then 'crossdev -t arm-elf'. + Once the arm-elf verison of GCC is installed, the package can be rebuilt with + 'make'. + + To program the board, the Philips LPC2000 Flash Utility v2.2.3 Windows tool + was used. There are Linux based tools for programming the LPC21xx parts, any + of which support the LPC2148 should be suitable. It may be normal, but I + couldn't get the board to program at speeds other than 19200 and 38400. + + ProComm was used to talk to the console port on UART0, and HyperTerm to talk + to the console port when using USB (Don't get me started on how crappy + HyperTerm is. I *DESPISE* this abortion, and figure that Hilgraeve must have + pictures of Gates with a goat or something. We can argue about MS quality + all day long, but HT has all the "quality" of a 6 year olds first programming + project in QBASIC. The only reason it was used was because ProComm can't + talk to COM18, which is what the virtual serial port appears as). + + If using the USB virtual comm port under Windows, the 'usbser.sys' and + 'usbser.inf' files may be needed. Often, these files are already on the + drive somewhere, and Start->Search->Files can be used to located them. If + not present, they are included in the ./Windows directory in the package. + + Under Linux, 'minicom' should be able to talk to both the serial port and the + virtual comm port the USB port appears as. Bertrik's wiki, located at + "http://wiki.sikken.nl/index.php?title=LPCUSB", has a note about using LPCUSB + under Linux. + + The default baud rate for UART0 is 115200. The baud rate selected for the + USB virtual comm port is irrelevant, and may be any speed. + + +Rebuilding it: + + Simply typing 'make' should build the entire package. The FreeRTOS modules + will emit several warnings about type punned references, which can (safely?) + be ignored. + + If you wish to use UART0 for the console port, edit ./monitor/monitor.c, jump + to near line 938, and change the "#if 1' to '#if 0', then recompile. + + If you wish to change the baud rates for UART0 or UART1, edit ./main.c, jump + to near line 62, and change the rates. Any standard baud rate should produce + usable results. + + 'make clean' will clean the project, removing the ./*.hex, ./*.lst, ./*.map, + ./*.elf files, and ./common/common.a, along with all *.o and .depend files in + any sub-directories. + + 'make tags' will rebuild the ctags file for 'vi' (and no doubt emacs, if + you're one of "them"). + + +Hardware (required and optional): + + Olimex LPC-P2148 board (required) + USB cable (optional) + Serial cable (optional) + GPS with NMEA output and serial cable (optional) + + +Using it (Windows): + + For the purposes of these instructions, it will be assumed that COM1 is the + serial port on the host PC, a USB cable is connected to the LPC-P2148 board + and the PC, and that the Philips LPC2000 Flash Utility V2.2.3 will be used + for programming. Please note that Windows 2000 was used, and that dialogs + for Windows XP are probably slightly different. If you're using Vista, I'm + surprised it can stay up long enough for you to read this document... + + Connect the RS232_0/ICSP DB-9 on the LPC-P2148 board to the comm port on the + PC, using a straight-thru serial cable. Set both the ICSP slide switches + (located near the RS232_0/ICSP DB-9 connector) to the 'on' position (towards + the DB-9 connector), then press the reset button (located next to the ICSP + switches). + + Configure the Philips utility for COM1, 38400 baud. Click the 'Read Device + ID' button. LPC2148 should appear in the 'Device' text field. Note that the + device ID has to be read to set the value, as there's a nasty bug in the + utility that prevents selecting it from the drop down list. + + Click the "..." button in the 'Flash Programming' block, then locate and + select the 'lpc2148.hex' file, followed by clicking the 'Upload to Flash' + button. At this point, the flash image should start being programmed. + + When it completes, set the two ISCP slide switches to 'off', and press the + reset button. The 'LED1' LED should start flashing. + + If Windows already does not already have the 'usbser.sys' driver installed, a + dialog will appear regarding the discovery of new hardware. (I don't + remember how the dialog goes, so you'll have to infer your way through this + process). When prompted for the driver, navigate to the ./Windows directory, + and select 'usbser.inf'. This should install the driver for the virtual comm + port that will support the USB port. + + Right-click on the 'My Computer' icon on the Windows desktop, select + Properties, then the Hardware tab, followed by 'Device Manager'. Click the + '+' on the 'Ports (COM & LPT), and there should be an entry for "USB CDC + serial port emulation (COMxx)" (where 'xx' will be a number). Note the COM + port for use with HyperTerm (see previous rant). + + Start HyperTerm (Start->Programs->Accessories->Communications->HyperTerm) + (see previous rant). When the dialog appears, type a name for the connection + (The COMxx name is a good choice). Click the drop-down box under 'Connect + using'. Select the COMxx port name from the drop-down list. Click 'OK', + followed by File->Save. Now click the third icon from the left, which looks + like a telephone with the handset on the hook. + + If all went well, typing 'help' should show a list of commands + supported by the CCI. If so, congratulations! You can now play with various + commands. If not, there's not much advice that can be offered at this point. + + Baldur Gislason informed me that the Philips Flash Utility has been replaced + by by Flash Magic (http://www.flashmagictool.com). I gave this a try, and it + worked well enough. It's a nicer interface, but it seems a tad slower. + Rather than go into detail how to use it, I'll just say that I set the devce + to LPC2148, interface to 'None (ISP)', the oscillator frequency to 12.00000, + and checked the 'Erase blocks used by Hex File', and it just worked. + + +Using it (Linux): + + Eeek! This needs to be written. + + (Richard T. Stofer says that Debian plays nicely with the LPCUSB code. with + the virtualized comm port appearing as ACM0. 'minicom' can talk to this + port). + + +Hardware thingies: + + The two pushy buttons on the LPC-P2148 board (B1 and B2) enable and disable + LED2. Pressing B1 should light LED2, pressing B2 should extinguish it. + These buttons are connected to the EINT2 and EINT0 lines, respectively. The + associated code demonstrates handling an external interrupt, and toggling an + I/O pin in the interrupt service routine (ISR). + + The potentiometer, AN_TR, is connected to ADC0, channel 3. The 'sensors' + task checks the value every 100 milliseconds. The software divides the pot + into 4 zones, each covering about 1/4 of the range the pot may be rotated. + When the pot is fully counter-clockwise, LED1 will be on for 200 milliseconds + and off for 800. When the pot is moved to the 2nd zone, the on/off times + become 400ms/600ms. The 3rd zone has on/off times of 600ms/400ms, and fully + clockwise is 800ms on, 200ms off. + + LED1 is controlled by the LED task. This is a lower priority task. Each + time a single on/off cycle has completed, it's message queue is checked to + see if the blink ratio times should be changed due to the AN_TR pot changing + zones. + + LED2 is controlled by the B1 and B2 pushy buttons, as mentioned above. + + The DAC output on the AOUT pin changes every 100 milliseconds by 1/64 of the + range of the DAC (0.0515625 volts). The generates a sine wave with a period + of 12.8 seconds, or 0.078125 Hertz. Hang a 'scope or DVM on the AOUT pin to + see the change. + + The I2C routines are setup to talk to a LM75 temperature sensor. These can + often be found on old PC motherboards, or as samples from National. The I2C + demo code is a simple polled approach, and does not take advantage of either + interrupts or the I2C state machine. The 'lm75' CCI command allows reading + and writing of the configuration, THYST and TOS registers, and reading of the + temperature register. The 'lm75 mode' command determines if the registers + are read using an I2C repeated start sequence instead of an I2C stop then I2C + start. Repeated starts are faster, and allow for holding the I2C bus in a + multi-master environment. The default is repeated starts (mode 0). There is + one potential spot for the code to hang. If the I2C bus fails to release SCL + (if the I2C device is powered down, perhaps), it will hang waiting for the + status interrupt bit to change. Any hard while loops should be wrapped in a + counter or timer check. + + Demo now includes watchdog timer example. 'wdt test' enables the watchdog. + If no command is typed for 10 seconds, the system will reset. 'wdt status' + can be used to examine the current watchdog state and the RSIR register + (which allows determination of why a reset occurred). Use 'wdt clear' to + clear the RSIR status. + + +Sort of hardware thingies: + + The SWI demo code is taken from several different projects, and culled down + into something I felt was more readable, and better for explanations. The + CCI 'swi' commands allow setting the state of, turning on, turning off and + toggling LED2. The commands starting with 'a' use the assembly interface + (assembly sequences are used to affect LED2), whereas the commands starting + with 'c' manage LED2 in C. Note that the pushy-buttons also toggle LED2, so + there can be some interaction. + + +CCI commands: + + If you're a Linux user, most of the file commands are fairly self + explanatory. If you're not a Linux user, you should be, because it's better + on our side of the fence. The file commands require that a MMC/SD card be + installed in the MMC/SD slot. BEFORE USING THE FILE COMMANDS, USE THE + 'mount' COMMAND TO MOUNT THE MMC/SD CARD. Note that the first partition on + the MMC/SD card will be mounted, and this is the only one supported. It must + be a FAT12, FAT16 or FAT32 partition. + + If the MMC/SD card is not formatted with a FAT12/FAT16/FAT32 file system, you + can use the 'mkfs' command to create it. If anything already exists on the + card, 'mkfs' will wipe it out. + + Note that while a fairly good success rate has been obtained with the cards + on hand, one Sandisk 64MB MMC card did not work. Not sure why, but the MMC + drivers are probably not handling something quite right. + + 'cpcon ' allows a text file to be created from the CCI. Enter text + until you're bored, then type ctrl-d save and exit. Note that whatever + characters are typed are saved into the file verbatim. This means that + characters like backspace are actually put into the file (feel free to + improve that code...) + + Before creating any files, you may wish to set the system date, so that files + are date/time stamped properly. If a GPS with NMEA output is connected to + RS232_1, you can use the 'gps' command to verify the serial connection, that + NMEA data is being parsed, and that the GPS has acquired (required for the + date/time to be set). If you have no GPS attached, you may enter the date + and time as parameters. 'settime 2007/07/08 22:51:25' will set the date and + time to July 8th, 10:51pm and 25 seconds. No timezone info is applied, so + date/times acquired from the GPS are UTC. Date/times set manually may be set + to local time or UTC (or something completely random, if you're into that). + + The 'thruput' command allows measuring MMC/SD read and write performance. + Eight file sizes are used: 1K, 8K, 16K, 64K, 128K, 512K, 1MB, and 2MB. A + temporary file is created on the MMC/SD card. Measurements can be done one + of four ways: 'noints' (fastest, but disables all tasking), 'normal' (CCI + task priority is not changed, no tasks are suspended), 'suspendall' (all + tasks are suspended, no context switches made, but 10ms interrupts still + runs), and 'high' (CCI task is elevated to highest priority for duration of + test). Oddly, writes are faster than reads when not using the 'noints' mode. + I have not yet researched why. Leaving interrupts enabled *seriously* + impacts the file system performance, nominally by a factor of 20. + + Richard T. Stofer noticed that this slow down only appears when using the + 'usbser.sys' drivers under Windows. When using the Linux ACM drivers, or + when using UART0 as the console port with the USB cable disconnected, this + problem does not occur. We can only assume that the Windows driver sends + lots of (needless) packets (yet another reason to switch to Linux!) + + The 'date' command will report the current date/time from the RTC. If you + have an external 3V battery plugged into the BAT connector, the RTC will + preserve it's values across power-downs. Regardless of the battery presence, + date/time will be preserved across resets (as long as power is not removed). + + The 'sensors' commands reports very little useful information. The sensors + task executes every 100ms, and samples the ADC connected to the AN_TR + potentiometer. Every time the task runs, the sensors counter is increment by + one. If the AN_TR pot is adjusted far enough to change the zone, the ADC + changed value will increment. See the section above on the pot. The + associated code demonstrates running a high priority task with a constant + execution frequency, sampling an ADC, and sending a message to another task. + + The 'mem' command displays the various tasks running, and the amount of + unused stack available to each task, along with the task priority and such. + The associated code demonstrates the 'vTaskList' RTOS call. Note that in a + 'real' system, leaving the task trace code enabled (configUSE_TRACE_FACILITY) + imposes a slight penalty on context switches, which may be undesirable. + + The 'iap' commands allow experimenting with the In-Application Programming + (IAP) code. The demo code demonstrates preparing, erasing, writing, blank + checking, and retrieving processor ID and boot loader version numbers. IAP + deals with primarily with sectors. In the LPC2148 (which has 512K of flash), + there are 4K and 32K sectors. The CCI will not allow selecting sectors that + are used for code. The 'fss' command will find a safe sector to use with the + 'iap' commands. Once a safe sector is known, you can erase and fill this + sector. The blank checking will work on any valid sector (there are 27 in + the LPC2148). The IAP_COPYRAMTOFLASH (writing to flash) is demonstrated + by the fill command. Whatever size the sector selected is, the 'fill' + command will fill the entire contents with the supplied byte value. The 'md' + command can be used to dump the sector contents to see the effects of 'erase' + and 'fill'. The 'stoa' command is used to convert a sector number to an + address for 'md'. It is strongly recommended that you become familiar with + the section on IAP in the LPC2148 datasheet before using these commands. The + IAP prepare and compare functions are not CCI accessible. These are handled + internally by the erase and fill code. + + +MMC/SD notes: + + As mentioned above, I have a Sandisk 64MB MMC card that the MMC drivers can't + seem to recognize. I have 4 other cards that work fine, one of which is an + MMC, the other three which are SD. I'd like to resolve this issue. + + During the 'thruput' test, when interrupts are left enabled, I have on rare + occasions seen a read or write error occur. I believe this is because a + context switch is taking place during some time critical code. + + Ideally, interrupts would be disabled. However, disabling interrupts makes + an RTOS merely an OS. The 'real-time' part means predictable response to + interrupts, and the executing time-critical tasks on-time. In this code, the + MMC/SD code is non-reentrant (only one task may read/write the card), and not + time critical. If multiple tasks have to write to disk, this would + currently have to be handled by creating a task that communicates through + queues to other tasks, and manages the MMC/SD card. + + Note that if a GPS is connected, a message that a NMEA checksum could not be + found may occasionally appear. While the actual test is being run, the GPS + task is not processing messages, and the serial buffer overruns. When the + task is allowed to run again (between tests), partial NMEA messages that + cannot be parsed may be present, resulting in the error message. + + +GCC notes: + + This code was compiled with -O3. This results in code that's about 16K + larger than -Os, but has a measurable impact on the MMC/SD card throughput + (not large, but it can be seen). I went with -O3 because with 512K of FLASH, + and 144K or so used, there's plenty of room. + + I'm not sure what the compiliation options for newlib are. 'crossdev' + compiled those, and I suspect it was with -Os, since embedded systems + generally tend to consider size over speed. + + GCC is an amazing package. I'm used to running into compiler issues with + many of the micros that I work with (SCCS, Microchips C18, etc). It's so + nice to have a compiler that produces code without problems, and doesn't have + idiot front-ends that can't even get the sign on an enum correct (at least + Microchip got it right for the dsPIC and PIC24 parts, which used GCC. C18... + Don't go there...) + + There's a trick to writing Makefiles, and I don't have it. It's an arcane + art, and involves the slaughtering of goats, black candles, and full moons. + The Makefiles I did write are very basic, and use recursion ('Recursive make + considered harmful!'. Foo on that. Worked for me). + + I tried a couple of approaches, and either everything built anyway, or + nothing built at all. To make the linking work, as files are compiled, + they're dumped in to ./common/common.a, and main.c is linked against that. + So far, the expected bite on the butt for doing it this way has not happened. + + It would be really neat to have Makefiles done right. Alas, I don't know how + to do it right. So if the common.a approach looks really ugly to you, that + means you probably know how to write Makefiles that handle sub-directories + correctly, and you can tell me how it should be done :) (With examples!) + + +Notes on newlib: + + I wasn't happy with the newlib syscalls.c that was included in the original + LPC2148 port. I more or less completely rewrote this, with the exception of + _sbrk(). _open() can open the serial ports, USB port, and FatFS files. All + supporting functions except _fstat() work (see FatFS complaint at bottom). + + Newlibs method of converting a file descriptor (as returned by _open()) to a + slot (which points to assorted info for that fd) uses a loop. As this is + done on EVERY read and write call, unnecessary overhead is added. I fixed + the find_slot() code to cache the last fd, and if it's the same on a + subsequent call, the search is skipped. + + _open() is VERY suspect, in that remapping of FatFS f_open flags don't + correspond cleanly to Unix's open() call. I handle the four common cases + correctly, but lesser used ones may result in a EINVAL errno on open. + + Opening FatFS files is expensive, RAM space-wise. Each open file uses a + FatFS FIL structure which contains a 512 byte buffer, plus some additional + space. With 32K on a LPC2148, and 20K being used for the FreeRTOS heap, that + leaves 12K that has to be used for the stack, heap, printf(), etc. Don't go + wild opening files, and be sure to close unused files to free the space. Use + open() instead of fopen() whenever possible, as the FILE structure has it's + own set of buffers. + + There may be a way to figure out how much heap and stack have been used under + newlib. If there is, I haven't figured it out yet. I'd really like to be + able to make a newlib or system call that returns total space available, heap + used and stack used. + + Since stdout and stderr point to same place, it's wise to close stderr and + set stderr to stdout. Only functions like assert() use stderr, so + intermixing stderr and stdout shouldn't be a problem. Especially since + there's no redirection of I/O... + + +Header file for LPC2148 (lpc210x.h): + + The original LPC2148 header file was very lacking. Not only were individual + bit fields generally not defined, major peripherials weren't defined. As a + result, FreeRTOS, LPCUSB, FatFS and newlib all used local defines. Worse, + some of these were simply "SOMEREG = (1<<31)". Great. What does bit 31 do? + + Personally, I feel the best way is to define structures AND #defines. It's + good that a structure has an element called 'CLK', but setting CLK to 0 + doesn't give a hint as to what clock mode is being set. A #define for + XXX_YYY_CLK_BIPHASE, then saying XXX_YYY_bits.ClkMode=XXX_YYY_CLK_BIPHASE is + a lot clearer. + + I defined a mess of #defines, with the majority matching the names in the + datasheet. There were few that become triplely redundant, such as + WD_WDFEED_WDFEED1. This was reduced to WD_FEED_FEED1. The characters prior + to the first underscore define the module, with the next set the defining the + register name. Subsequent fields define the bit field name, and then + possible variations. + + What I don't like about using #defines to define the registers is that the + programmer needs to know if the bits being affected need to be AND'ed or + OR'ed. Structures neatly solve this problem, but defining all the structures + takes a lot of work (yea, IAR has already done that, but it's not legal to + just swipe their nice headers). + + With one exception I'm aware of (in the TODO section), I rewrote all the code + that sets registers to use the values in the lpc210x.h file. + + While I doubt it makes the code more portable (will NXP change block + addresses, but leave bits the same? Probably not), it does make it more + readable. + + One of the last major areas to be completed is to pull the USB protocol + engine #defines into lpc210x.h. These are currently defined in one of the + USB header files, and have poor name scoping (does "ACK_STAT" tell you what + module or register it applies to? No.) + + +Software notes: + + All copyrights are by their respective authors. FreeRTOS is by Richard + Barry. LPCUSB is by Bertrik Sikken. FatFS is by ChaN, with sections by Joel + Winarske. Other sections of code may have come from the intarweb, and have + respective copyrights, indicated or not. Any code that I personally authored + is free for public consumption, unemcumbered by any copyrights, etc (that + crap is just too confusing. BSD? LPGL? GPL3? Who the hell knows...) + + I've re-formatted a good deal of code in LPCUSB and FatFS. Some portions + were re-written, others simply re-formatted to my coding style (which I + jokingly refer to as JC1). I have occasionally whacked comment blocks that I + didn't really think indicated what the code did, or was redundant. I + probably whacked some text with copyrights in the process. This in no way + reflects an attempt to claim the work as my own, or to otherwise dishonor the + original authors. As Isaac Newton (more or less) said, "If I have seen a + little further it is by standing on the shoulders of Giants." Without the + work of these most excellent people, this code would not exist. + + Most of the reason for my re-formatting is my way of understanding the code, + going through it section by section. It's good because I have a better + understanding of it, worse because it makes drop-in replacement with updates + from the authors more difficult. + + The most affected area is the SPI handling in the FatFS code. Originally + named 'mm_llc_spi1.c', I felt this was not cleanly integrated, so I rewrote + it. Maybe it's not better, but it is different :) + + The FreeRTOS code is almost completely untouched, except for moving an #if + around that allowed compiling the trace code (configUSE_TRACE_FACILITY) + without requiring the task suspend code (INCLUDE_vTaskSuspend). In the end, + this was probably irrelevant, since I compiled the task suspend code in + anyway. + + +Things I wish were different: + + A collection of random little thoughts of things I wish were different. + These are just MY opinions, based on the way I do things. It's not to say + the original authors were wrong. It's just the way I'd make things, if I + were smart enough to write this stuff from scratch. Most people probably + shouldn't even read the following list... + + While FreeRTOS attempts to isolate the user from system data structures, it's + a little *too* aggressive. These typedef'ed structures should be in a header + file, so applications can at least use sizeof() to help determine memory + allocation. Everything is done through recasted void* pointers. + + It would be neat if FreeRTOS had a xDelayTaskUntil() that also took one or + more queues as a blocking item. I want to run a task every 'n' milliseconds, + but if something shows up in a queue, process it early. Perhaps this could + work like Unix's select(). Or perhaps a variadic function that takes + xQueueHandles as parameters. + + I dont' care for FreeRTOS's use of portBLAH typedefs for portable types. The + world is pretty used to U8, U16, N32, and BOOL for unsigned char, unsigned + short, signed int, etc. FreeRTOS also seems to require declaring too many + things as 'signed', which should be a default. I'm sure this is done for + portability, but I find the types are not as intuitive as they could be. + + Another minor itch is that all function names and types start with 'x'. I + would have preferred 'freertos', or better, a user-definable one, via a + #define macro. I think programmers forget that their package may be + integrated into a larger system, and while their naming convention works well + for their purpose, it may not scale well. FatFS and LPCUSB are guilty of + this as well. + + FreeRTOS has a couple errors in several of the modules regarding type + punning. I understand what type punning is, but I don't know how to fix it + safely. Perhaps RB will fix those up in the next release. + + FatFS changes return types too often, particularly for errors. There's the + errors from the SPI routines, the MMC routines, the disk routines, and the + top layer FatFS code. There should be unified errors for everything, so that + errors can be cleanly communicated up the stack. + + FatFS can't go from file descriptor to filename. I haven't figured out a + clean and reliable way to map a fd back to a filename (FatFS f_stat() needs a + file name). I thought about malloc()'ing space in the openFiles_t structure + and copying the filename when the file is opened. However, paths can get up + to 128 characters, and only _fstat() needs that information. This seems very + wasteful. + + FatFS wants the user to provid the get_fattime() function. FatFS should + really have a file for locally provided functions. It's already specific to + the SPI or IDE port implementation. As such, it should be stubbed out to + return 0 if the function isn't provided, or allow the user to implement in + the platform specific file (basically, an equivalent of syscalls.c, but + internal to FatFS). + + FatFS has naming conventions that I don't like. It exposes too many internal + function names that should be declared static. Structure names like 'FIL' + are ambiguous, and too likely to collide with other libraries. Functions + should be preceed by the supporting module or library. All FatFS functions + should be in the form of fatfsOpen, fatfsClose, etc. + + LPCUSB is not organized quite the way I'd do it. The USB protocol engine + defines need to have their names modified and moved into the lpc210x.h. + + Newlib lacks certain calls on the ARM7 platform. sync() and chmod() do not + exist, while mkdir() does, but wit no corresponding _mkdir() support in + syscalls.c. Returning ENOSYS is easy enough, and doesn't take but a few + instructions. Providing support for a wider range of calls would be good, + taking into mind things like FatFS that provide file system support, etc. + + The Olimex board COMPLETELY lacks documentation (at least, that I've been + able to find). It's up to the user to guess what the slide switches are for, + etc. It's probably pretty obvious to someone who's used the LPC2000 parts + before, but if you're buying a board to get started, you have to waste time + figuring out how to use it. Even a simple Xerox'ed sheet would have been + sufficient. + + The Olimex board also lacks the ability to control the BSL line from the + serial port. Reset works, but without being able to flip the BSL switch, + it has little value. + + The buzzer on the Olimex board is useless. Had it been connected to one of + the PWM outputs, some simple sound synthesis would have been possible. At + best, it demostrates you can toggle I/O pins fast enough to make noise. + However, unless you want clicks in the output, you have to disable + interrupts. And why *two* port pins? One would have been sufficient. I + would have preferred to have two more LEDs, rather than the buzzer. + + +TODO: + + ./FreeRTOS/portable/GCC/ARM7_LPC2000/port.c uses local defines for timer control + Finish rest of syscalls.c functions (fstat ()) + Some modules (USB) still have local #defines for hardware (protocol engine) + Add VIC software interrupt demo (?) + Add data abort demo + Frequency measurement? + Add fast GPIO (beep speaker) + Fix SPI code to be thread safe + Fix I2C using polled mode for EEPROM + Add firmware update from file system diff --git a/Windows/usbser.inf b/Windows/usbser.inf new file mode 100644 index 0000000..4267aaf --- /dev/null +++ b/Windows/usbser.inf @@ -0,0 +1,45 @@ +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Provider=%LINUX% +DriverVer=08/17/2004,0.0.2.0 +; Copyright (C) 2004 Al Borchers (alborchers@steinerpoint.com) +; released under GNU General Public License + +[Manufacturer] +%LINUX%=GSerialDeviceList + +[GSerialDeviceList] +%GSERIAL%=GSerialInstall, USB\VID_FFFF&PID_0005 + +[DestinationDirs] +DefaultDestDir=10,System32\Drivers + +[GSerialInstall] +CopyFiles=GSerialCopyFiles +AddReg=GSerialAddReg + +[GSerialCopyFiles] +usbser.sys + +[GSerialAddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[GSerialInstall.Services] +AddService = usbser,0x0002,GSerialService + +[GSerialService] +DisplayName = %GSERIAL_DISPLAY_NAME% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %10%\System32\Drivers\usbser.sys +LoadOrderGroup = Base + +[Strings] +LINUX = "Linux" +GSERIAL = "USB CDC serial port emulation" +GSERIAL_DISPLAY_NAME = "USB CDC serial port emulation" \ No newline at end of file diff --git a/Windows/usbser.sys b/Windows/usbser.sys new file mode 100755 index 0000000000000000000000000000000000000000..b390c36e335e50efc4dafeeb399886e88cf7b449 GIT binary patch literal 22766 zcmeHvdwf*Yz3-Z20uvaRK@*K7N>Hp(5R*(EGfWaDBoih;V8W1@pgh83LJ~s~lRXHP z=)_H8cD563wbZsA>Zy-<>gnUu9za+aG3$j4>lFRb}h|p7e9^`Ug6l{Dc(lAR7{aEjLeDEfge zR(j&bpJNZoWIHPu^HKu0qeR9_ey4qmwNkrZVJ!WUvM+ljJkNazz(OIoQ$YFSS84b@ zp+6^nlEGO0!nUoOgiXK!ouN9z`g0;tKK*$qT*bYE9Ad)N z6~fF4AxWwpjadiX=MrMma5uz^;ov!7)JKRA*v%C}M#V(n^g;K>#@JQ3C(?%>^AA7f zAAZbwkH>Mg4LIDRyE7UMk;)M-2sS)UDQ{zjPxwiuZmZn=DDuNQ4H=d$5RHkSO|x`efkd6$=f|Vbdb{ntI%zMZ z>!f`(cu4ym=AVY2HN}&zIvdZDKN0_uQRn(oLh~jdbq;Dvivm#H>ImcQSj1`>7Nx zDE6Pi(-4};`R0UQnF|JuP0`*WeYKdJYN`=UcMXIr-p)ZgGQ55@H5XYMHynV>8^F9U zGr4SDieDDyr37Sqm%2Np(NKfwNbNT!sQGH@DfCBt#)z+(9NoC#l@yvu;In|-VV0{+ zwqx?$wNmA%e0!#4f4O1)A7W) zi>3o zPsyL2eLB23k?k1}nuxxYCfQXRb&ZUg&#u}$c#1+0$wYvU?_!Oj&4xUDiYSRc3yCG733Qfu*Xb+@TLuL(MO0*l*R5NhL z6pgKyWvWT$ofJPYNa9JVc)9b?eM{y0fVF&KZDi>wsz(l)sVcu*P8gq>reW;m7;BaL z$Et<|O{9LKh1p|7yOd(`AQucK_BuvPcBjyo)nv-?0YAPL z?dGhC3I;iE8#K}ESA=B7{7OCS3{{wyrrfj`0%=egDNAz?e~z> z7It`qERS%jCv?~3sX?r-HAOR8Yr{vz2#YP942fw>3Wbl1t|xrTO#lq{Ys_#pT6K04 zW8P=>EB|r>sxS_^RRe~%(cn{4Q!u=}Zgm2E#WPmYbP&D0uB-qAPdY&4lg>YcrXBbW z=GssnaDvvU760eFK*T*9@D5IyJy>o?R#S&D!{pLhx!MdCg|u(9%AZDE>4V?^onhD7 z2BM{D2T{%0=fq_uRa|CP!^@Hqz~6PzB?$%y$pjaZ{6_hNn))a;JluJa7$h{yHsJMo z(OzQ`a5{sQfPET43~tzn`ER1BZUFUDG|9l_0!WV>(-=RkpfpLbc&SfbH_ud!;lC%- z_CaG>wIZ9r9K+6!Eqh|Nu)G1ir}w3h{UG;9gBuLB*}jqUi$vTddGI;~)GJ{Y|7%n_9O{Qco{0}IH% z)~1c^J)0_ZjD}zKl9m?COFLDD1hU#<;B!Ecp3yQx=BV2WRWUGh3oJ!BuNBA#)DRTl zgY{RK4Xplp+`Q^L;*pd{SD~t^R9L@|N)~A)a$Xa@87l|BybpMMkJKroNYv-m0|W() z24tS|)=BHnN$W@OI3ukemgcDMI1fv1tL4Ek(8O09>$>n3M<+ES2tzh+K86sZXGUk7Rrx z<;qbt$%~=wJwDh;gK3Blo}5P$zmY{mVtdGg(%lC29eO9Wg$UtkV!e3Uqo<{VHAqPZ ziR-0<#J5p#EAGCMm2M1`?L9ef4e+30qEVz#aIA+xB~OLrlMLFPM#It^*=^+l%uE^` z!V6N-4`HsBH_0XGlG`-@Eo3H^w_fQNr1DnDFXX(XiGdBI0Z2AOIGg-|Mz6dxvymy? zCXCN;*LlmH$BCmGuRvZK<(;$*I8O@s8Rg6T7;~D$(@92nS+qRu3V=%G8PbOI!SdyX zI;nhxR6b8xn~z+#$uIC*1Hyn%m23dIZ`AOH1ui_pB)k&)#2d&5} zk=CEK?D-3Lf-9sE7P%)aywk{bY-W0 z!3>2-ZRzzS^sGwgbtU#JO@yJp)V$-0$g1?HK~)XRB_2v~E;YA(XnA33;>bn!2-@nc z99iTswtc%!+>!t*9cF(Wah(~IluE72ZF!tq-DdUhCn0m>3*p;!ZPQ-v_Rdohj#+UJP6ITub#?#7`0| zT}epR$xBCV$LgGiq)L-ic}}WS>ZI=5Q6S8)yfEOcQtq8XW27`rqwz;tGYBO2DU$cx zpIc>=R@TaW;=6E%%0l!q6ECv+jIQ#*f zbX%Bb4(MK#yssA6$o=bZ7upHW`o?MMe&S1?V*4@_N$AE=eAxKEg(6AxIEp|E!=YgX z2dtaO6}O+|ox0mj7DxCQ8WJY%N@Kfs$6dd6L;o-|zCJ1HI=N$QFw&He_|Rz+<9s)0 z$P$EGZEtm{J8qKsB$M~iOzQ1kihJT~cH=Z*>JsO>ZRcaUf1c2opbx4>K)h1P_04= z3H*2AW=@x<5cGgm%(x(SSb2%{(vr_MLSOeHsw; z3-e&@MY>JkjA-{6+&_n)L&n=E^ae;XN2vag}%kWoa74M0gDvxzLanOZizsJPe{wLkcf^(dPi4 zL*H@*v1XpJ7hXrtG|KBeuJJK(%w!n6nWQhk9Dbco@_NKtOC#kVlQ7S!rk=yNqh~qt zjGA-HSMiZVAf7>xd(%!NnWm(23D_OCkbpD{4R11jM6g6obo*UpR!Rs~3-qDC> zH1U;AJ3)nJM_Ku5xBKQ)s%xZq$4aLTp0)iItRJ6hky+^ZLuqz(U1k1+%(jqIOx4K=PhX$#01IsjkW>8QMb#u zciv%@y=mYrScC=66PQS$*1j!49geX+HRAP+H?))?Zsv~rzysF0e znqZbZX}mCv=HAk*=}U8kX_2MGbus3VcS6cPaMLL^V| zD+|&{p-_j_cgfHn6_P0QGFL95NR%(qP5tzF)ps51M?=%`PF%-C1B| zpBD~<8KogBj4r>@k^(j!Gh?-JS9c)(@)+vI?3@}cuYr4v!NcrRZ`^}i^~N6D>9=DQ zW1DfU#6`b;Jinu-SFB*HZ8>9maD5-wPjMZ^bqd!xTr*KV57!c;Z^u=ObStiIT#w;; z3fIqZ4dVJet`S_;O2&%u+>GY}d~+|JJ-EJ)Yd@}|xPFW4BV5*%s2^83t{PlBaXpA@ zFRuN#-o|wfml61;;JOai0$k~Y8?KMAe)C&gnW*a*c)IYs+%LZNV??10A=r2s z#)CZ!BZ_{*HQF;?rf2ia@W#ny7%zt95dM4k;`gyWcFE5G2tz_5wf+Bp|Mw{XNg$r# zQ7I@lGdyPmr^wjXG@*2FnsjeE40uiCiU%ngX5dRL!nK;feHjcfKt?2BH5oJH!(eHJ z2vf={&2Ypd_c_^PlD$UDewaivv2r4`dqP`fWV_6_8o>g>N>MQix5=S%h;cPeMWk^o za4Ij+1jlmBSqfZn_Y265#BK;bX(G;(ziY&!>pj5=)10PtZ0ev$B?g-cYj*}v9NLRl zwD+P3jiFKJp}TJqt|_9XEnP2w$+*g!kM+u-MnL-f$`<(eLmQBO(F!2IUk`XQhkqar z|E~nU2r%`uv>Qu%F--$45oQ;qaL{!C)%EEaIF>^*0F0)n4}K+UI&d24RBr%+macOE zCE5?gka#getLaAJ>Y{XB(-p`CS{JFwsFwC%Y7HGE=u8bdL$DOhSb-y(daXa$G9jY9()r5y3zp+Ck^Q$Vz#6L@>EM*D=& zU&B8c#qe|V!{EB1pJ>g!Pc(Xx;A=H_LVq2He;x2ZznLI9HRx_?D@4%sWBtrt~eU?gZ)*!DiBTbI+i@V1mcM{|>KweLfB=3;E zjv&t>eQOxSmOXz&QdG>AJ#Qel?km_-l)Tsg=^Z&oVi~_p`&sb zdf7b!0|rs2C-=at82U%#zf9aU^ev>sPfcw%h#ezLxRxRzr5(N=gVH|_Jct=uVvLrj zW0+X1h1ZGs91DiGbgrs3)f(dsyourIz$EZH@qOQ!+jsXj=;(ik4Ed( z3j#5k$by+!4g zsl#*afMN<6})WKG_2T>vHi)GLl&%7S<4jj~rZpx3kY> zh->i7;BrV8r{ZqpfPVZ=HnS!seab__WG9WH;}d%=o#ED#-feA#M(cF|(KK2f+fa+R z0eZeNOo;#85q^nA)=V^@=4l;y7NC4lWjzc; z-AY1qL99z-L=2@Hj5Z!D(CkREt;u4=NZgePM?kDihit7CcbbgtQx6c|#pYiUo{*=< zm+9jX0y6TI`%kO1Qli7L%1i74@}IY`aaJ>7d;ZzNipjscy3Joq0yA>yEg37Dsh*M1AFyk3HTV^U|7q3X50 z#}NS6J@Y#@qAEC^+$V@59*|2p)`av<$rRgxc*#;uHM)ALe z^^|d}GcSX6XS9q{9T?X=tx^5&z)ROWWauJ0W z;IO`XzMH;H5R8CKunizO;`JgJ-2FIm;l7MEUQe>`%?xt4G0Quygq%i;l_TOa^x;LS z19K%VA21UUY4?Kbl&4RDwfZ^tLZA`$6p@Q|(?VzKol|tVqkDr|xN9=F$nj2~uQZ~6 z87#-<^Dn^aJpx+rFEi0Gi8@w=Nt4ldgID=v4qRRYOIFM?V!5WuES03o6}957aoJ&( zr^##Jz4tzW@AUaKCKw><<3eHQM4_;dnCJ^4fdusBL;`1j&YGP8ugNTLKtI4u4 ziBoc6DubjFD1#JlEDICDuY6=056S5&;TaO|2#HHPfCAd+6K;YGLW|h@qy@L+QZqya z%w#CD(sUAYP+aX;v>PEwH$)N*EjjoW6FuCruyc?Pu)Z27SD!;`)5M!-sP(l!@HEN2 zp*`pP}duUt#C_SF|V%>Ew`E%mClez86HwXvvB% zFOJ7;kQI9}e9t^4T*X&92$EzUR;4qbNMpCX^xJG`qvd*_3^Imx0sxfsER#y1!bh|% zHeYN7W=)*SdCVJo#kApH>+qQ30^91BJVpxSD0?v;(C%@WO#1`Gpc$5y>@f%AWL-^R zL5OQA45pdulMogf8ZR7zE>=UEu}A9sA(K!zd0xo-NQF;cX^8Yu-Z=Zi4?#@h7mBK$ zheQzOM0g;tbMS3IX?k}*tWR1!FcS~OXRx-H;qo%rIxZx$E+fx62h&kEq}tO@L!#+o z5`$06N#g=-oWm#UjR}?Cd|3%hm806HA%njlg2^iRFX!ZKmzk567apQTD6XaB(K}sF zk59_KtM<#!c#@F+<&2M0+aK3>?U!ggPXMy%;&b%7oLJEYURp`(qS}104Q$LPw`P>j zlBP)IFiDLV2GWG2X1SaJv9P*;Xj^$L7Brz>UF}m3a`w9tdzH9F@jJkS6fXJ5>vbnvQIa)lBI-b37_yAzf7N}jq}oF_4$;}!3Ch5W+ueDgn>6$wqc#7*ITstM~`y71FB?U-mHVwwgTt+f7i5#_t*S+Usb(l>-e z%Z>PqY33KGh^2~hYqXrmh({W!X^`hv{E=}V9#}5UG}HYS!yKu6frf+p5UCBYz{)K7 zAyRzON-P)Y+DxM=co(zDB=(cx%t9%0Hx`%}JcWCc#eUk^gJrz~dq{ZQ6sa^-`RfO# z&_*v7-}6>Pk}Cu|6RXftA5{WV3Q=5rPNZNH1vq1YJVgyp%Jn zZ2Ku-15d4Etu?e>uC#W3LUIxz)liZ=1>sl6`b^=bF;R-DB5o`xWWh2>^Q+aFj`b?BrUcTYA$>)l*n9^X$_FqF}7w&QS1X;pDe-*LN*Nn zDH6HL7%8uj{Ocr>lmKt`rN*VqpvLy5-u;bHX>@B|$ zI8*P?Cw+Z%Oq=*P>Et3>SV0V$^m0BTS6FkL6)AaEt}tSROwufAnX$KeRQA}#T086U z-Z?p}EP+c%EumGLM`>Qe8N;CJ=bs;Yq z_+qs500CbzNMvB>GZl4A8+SW71$fIeZv)zrlu=u}GzxW>cAam}T8e0&%y$e+R?X0n z_`;R#t@x_csx*l&nqzG$j>s2Qr6$lt>pfqoAyF>7xOb|vkN5=jhy5)Ef@BcS%!4w3 z<}s6lh`q6;wbJtRfD{^WcwqHm-^wr|uA!;Y@-*A|Sa#h0kUDf9vG;gSHAL1av|_+; z>h}2ov$|*!UA^fyO9R|3^YJk+NA0RL{%r_UA$x6}t zke|cO3sPt}REYe3+K3^wrj4%%fylJ%83;srXjv$LP4ebI6?!(}Rqpr{Uk?t!9_5=x z8?)kys1kcz*U=zAN|E-)G?mJ;rXP}4<_fn)?lob9p9hqUl-vFF-+*}VVh56%x&m3$ zHSC7P&U!WVLA=9ehN5WSsZl;6lfquw zdYe!A1y;6o@ONo97M9yC8ba4=%Wbr~%!78ZjScYUL(~18hcof+_mQ`0La5^yt(0lr zCN#I177x$MflR42-Eo&uxGvKBI_S#Rn5!baWc5~gS1ZGx(168KF-8EyAqhWdQsoUO zPyRcN1rMO`v3H5Vb3DecdqyJF7Dluq9Q+*DMO>q}K4aKF1X^gJHLxx=4@u%F!0IMQ zQsjAtx3!UufwNCz%k6PJ2zQXyyEQXF4h=Xj2&=@4s<13jZ@mdz;vaN>W>7x+4{*6y z1Hgz_i@W@)^aw@rogU-vl>4U1G8H*ZZKjAAMk+QXBJRXf5?gV{oy(0$PW?u{)(=B@bP=6L3y8*zgUkV>vRf=Zlk zgp64A#qkjnVz9%K`%Jh4yO`z&)aR+UurY>}<-qp~Q#GGD;eK1HKIu3}hN>_}4kXuP_?(4SI1WG6Ef63CPH~_vd0Q>AQ$mD1}D&CO~5-RNd}7XZp3>; z?7>KQBjRa%tAKi-ra3J>ioIZFEb#nG zSJ)>Q67r7P@Je!9Cv=?lew`yb1an^3P^2B?FmW!4uv+Uyw3ah4w!#m=?-%(t}f~Li1sE=&uPShxl zd>*>}60jb(S$Pw1z18Fqy&>%zpPL|w>F>kWH{|BktD!qCsUBFgk7Ln$BCag|{<=A- zhyDlpfTKQ#Lng;Q+D#?gAa~OfTV>r6r$MBfbCSf1b$;sR{4}WXQ@O@Z*p@+zJK3bO zYTIx*7h-W0!H)TTR9$2*iJ95mP1Mv9sOY8NurS34Yq`iCSK9<+R^Z44* zSRSn{$>m0A@1sbJA0ENM3MkTx13e|!syixr(xH4u!=7t1=uw+~U!?mnzH8_38>BROmiVc$BSEr?hm0#jA41>p39ZHUtFUQPv)h(s+RoE{Vub7} zVbxmpR~Y91IzSTIvgZP7s;IE+`Lq7~4W8mqk}yjgnrZ2JA8%57+7OvhJyI3Njf@`E7lbd*UxG0}i1ttF2jM0NqJf<;$j+tN^OywLWt2S&!k(lAsgE3D*_9>ngD&Hy zr5ued@vT-KfG-|PL1L5=Q<3?7kn(oc6G)xPhi^qdjt z{yCmNbpfHeSiY4|#%V)qK>0SR*NU;r={Ius001G1tX}0lUh)gPYb8gKiuBVR<#W9C z#$xOR1o1Obms+5B0p2-yguwmkZ{XMPou*#a!rGX?RxpT_aNJWk}Y_)kCk1%?`ZF`G|_B-$Qm&?!myIsG5p*?5=$6_=jx*FkC;Jm z!x5oa#Yh*{?`4JAxuf-1 zbAf3=L7XyuP;uh07Nz3hi$3hiuKzj6MCN)wWGOt~uOjoR`lEX0Kk-b=RbeIQCbvuX=)#?~f-($C40`CuHn6AHb>E?)|(g17JpFU^diO&%0K=06lN9XFI-Tg=}LFY!!KDWB=IrW6)g?=hd?5D3IeU!Eh)IDc;!QgUX z?>6OvxciCe>KTTI_$>?AfMOyEg$9jbBl`#v)J^s(N8f>|a05=(aReiL2H0LQFkau-xl-c1j9TxiP><-SC?VH@)IP=1vA9_*Fabn(fp$l%z}?kGBsIZ^m8 zsfE~&RGx`Te(ebBm=~x?{wr+=4V}TZ(XnHGIKK=!nB~?q=LJjGUNk^Ted+6<4m@j1 z7inS2xs!ANazZ=Qyq)Qo8hjfBN+1mbN*COW2)E{aIJt^3l@0=iNie8sJv1t2;LU}q z21SHY8IHFy)F&}zCcIFJkSdS-31=z-$`h!_r|gCT@hN}8ozJIdL3;!(u!r?7T^$0C zX;JZ?sVY=D7B}GM4KaZCWJVsKbun+`0cs9Is6|F|d+}xdT?mJL)7XdlK|F&!KJoY@wpMEqnI_bL6tpRX7PMMDjq6D* z%{APPot+nHMB6j!>6Bxnn-0R!=q0}eQ*?$Gd7&hw3@?ywWJFH>uMMQ$Db75A5(Jl^ zBGmnyfA@z8=hU6lejizq@_xD_uz^t51}Pi=aiYq<8ea;4kg7-E@5E&S%mXZ_V~)_G zzgr<|mP7+}2(OUSm`nkflJ#^dMNQp}3gGTfp6B-f^$RCw5Ac(K^F|Rz5C>*U z8!~`%0uGvE$5*f0+*4kGGjCSpEyFW~`h&w$>oa;bc%%*UEZ@W)t`ucH{6HB*PKP0s zBxAo6)?lj8RxIgd>K5Nzw*Hk2SZi^+Ooe+9fIo3@0OOv{+12?6!QJQyC`no62N-=*^U>+e^fGX3XT z#n>tQZ=RzQ^1gvQ`hPtqC*++*-f`qz?)TsE96(({<@V}k{2QM;y_>de+T0YZ*uH67 z&==eyG_*9cR}IU970nGo!=|Q&uLXUr4b3Y6iT@UbuPCK|Q-*(!6#oRL2P#@hH#Khw zHdVB>vb}h#E8W!8RMyt8BiP2i#ot@Dw>AZZ;OgLAp?g}-uT?+phTJ;45tvPly!Har1S-dTG z2iu5-hmkjdgzQ&%N59&HmH7WvV^FVBZcuwisSsp2z>#`QbtDpf^nTF$TI20^SA~LY zyZoD)Lc!(1U93KFwa`R^UESQU1^-{nC$xb$*pE#48nIUgw>7j2`ZtW-ggUhX->&VO zTbdfStY~NkcbuXx0&NX6z!N{13;5EuAW)kOZy@-R*4AM2)=6!zK`%8N621U2UN#3k zDQ#+L4>CXMUxiw9zpkcVCB@TdT!rBs6Hnmr7E(H-WL4w3C?1r(vS2yEdy9wa8;MPfGm^}s&=jn?H zM3>7nqEjVESkQ=#!>}J1+gm|1p^g<%se7jYss^{N*wnTSGg%033$~Rt>}c2;WQ7yU z^`gp9lhDxGw2QInz-CFPp$W5*rV$3NrCDfeX^KzTAAouq<7?Esg_UjIwgiKFr?&;o zvt7oGnDpC2P0WPZ2ENq5*zfQi=R4o7b|JW(a#^#`(tc-KbJN1$P6Q%Wv*mUA%o>|h ztB|M9p0T`grq|>Rw$-(?Z6}da+7co;!aOA_7H(~7VplTgzOs$pO>LN_EzNZe+d^%d zNV1g$+qbkevV>f8k|aCO;Xf z+XLWQFJoKbHeLGLnwX0$KjJAHai=(w50R!-h}~?&b0sTBnts1D{`I2fFeNednnz6Z zpZVyyn3vMOXESn#Mt|jxS76!0@kPw14RM`jwhgs4uqMQJD)3f^s1IZNI7}AH#lQJ- z;L1g)0R2Fu>neO(3YhJz6>+3ywrf&6sgg&E+IZxs1$7B5gG09gnxapec%-QX-{GIh zv011q-ZsUV+7V?UoLVP9!3YAbOHfZGYM^MpFY zHvR=srWK{si*1OLK%w#ePtME0SEcM`-Y1HzU7lVGQBJtpale(P9C*VyLXPVyy$u>c z!Zl7udefqNe+%b(*=@kt%Uj*bLTG<-Oz?|im;7fl$MQPCXHCwOkA8l{hs&jvo`*hyF=Na>7lS$y zZCv!NML%2g;-ce={ch))|asJTxp7SH;s=}JWVBx)mj}<;u_)mrXg@+4Y zFZ^qvyJ$nv_lka8^l4E_@ioPBi*G5;DRvf@6hB`4Qt`*d;l+s+C$pP#9?1EJoFC-;OU@fPpXXd{3)dW;@qvdPvzo> zSKgI*bMkWXe0lrxKFGU}C+0tx|E>J9`RN5W7Ti`)R^TsaD)?H#!v#MsI8ZQ9Fj(;G zfVo!0n96`t9j-NRWIF1$m4%mNM=q+k50voU+ z!0&5W-Pw<1|2+HE?Bm(*W&bgIZq9u|e8g)Bb(?EA}_- zzXQ!bv(Io`H?#TO2-f!~X&%eFk2L(q8epT?hf(7;hd%OK1`*-Z` z+Rxg*;>dR_b}V!3a(u(_UB@$y3yv!nr7ya5QRAZBi@Fv)3=T?gUg5l{&{bGfcz59g zh2Jgwe&O4NUnyEvw58~=qF0Mbit?=9ZH__f9VvG~Kq7Z$734;N#P;xR2N zHS13FMMXc{*>`2{%KlgM;LYsQ*?-L*$-XP6Gw0hmZ|1z4Q({|XJ7@dc)}I^AJ(-)F zcTL`nd3kxxyo$U)-d%Z8-g9}!^UmZ=&!3xrv>;ZHWB-Z$1^X}UN9@P#@7PaaJTBNT z+LIhtIMN+ob!1^&iZL?F99~Djaff3QxaCd~YX6V@48X@qH~iXnwXf6s>4jT2Z(|Gb Kt`+<@y8kaD-!ydq literal 0 HcmV?d00001 diff --git a/adc/Makefile b/adc/Makefile new file mode 100644 index 0000000..497476b --- /dev/null +++ b/adc/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=adc.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/adc/adc.c b/adc/adc.c new file mode 100644 index 0000000..358a18d --- /dev/null +++ b/adc/adc.c @@ -0,0 +1,28 @@ +#include "FreeRTOS.h" + +#include "adc.h" + +// +// +// +int adcRead0_3 (void) +{ + AD0_CR |= AD_CR_START_NOW; + + while (!(AD0_DR3 & AD_DR_DONE)) + ; + + return ((AD0_DR3 & AD_DR_RESULTMASK) >> AD_DR_RESULTSHIFT); +} + +// +// Assumes PCLK == 48Mhz +// +void adcInit (void) +{ + SCB_PCONP |= SCB_PCONP_PCAD0; + + PCB_PINSEL1 |= PCB_PINSEL1_P030_AD03; + + AD0_CR = AD_CR_CLKS10 | AD_CR_PDN | ((11 - 1) << AD_CR_CLKDIVSHIFT) | AD_CR_SEL3; +} diff --git a/adc/adc.h b/adc/adc.h new file mode 100644 index 0000000..a17bf8d --- /dev/null +++ b/adc/adc.h @@ -0,0 +1,7 @@ +#ifndef _ADC_H_ +#define _ADC_H_ + +int adcRead0_3 (void); +void adcInit (void); + +#endif diff --git a/boot.s b/boot.s new file mode 100644 index 0000000..814c178 --- /dev/null +++ b/boot.s @@ -0,0 +1,367 @@ +@ +@ Sample initialization file +@ + .extern main + .extern exit + + .text + .code 32 + + .align 0 + + .extern __bss_beg__ + .extern __bss_end__ + .extern __stack_end__ + .extern __data_beg__ + .extern __data_end__ + .extern __data+beg_src__ + + .global start + .global endless_loop + +@ +@ Stack sizes. These have been determined empirically. If your interrupt +@ routines become more complex or use a lot of dynamically allocated space, +@ the IRQ and/or FIQ stacks may been be grown. The supervisor stack may +@ overflow into the system/user stack as the system is going any initialization, +@ before starting the FreeRTOS scheduler. The scheduler itself needs a small +@ amount of supervisor stack space, once it's running. +@ + .set UND_STACK_SIZE, 0x00000004 + .set ABT_STACK_SIZE, 0x00000004 + .set FIQ_STACK_SIZE, 0x00000020 + .set IRQ_STACK_SIZE, 0X00000100 + .set SVC_STACK_SIZE, 0x00000100 + +@ +@ Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs +@ + .set MODE_USR, 0x10 @ User Mode + .set MODE_FIQ, 0x11 @ FIQ Mode + .set MODE_IRQ, 0x12 @ IRQ Mode + .set MODE_SVC, 0x13 @ Supervisor Mode + .set MODE_ABT, 0x17 @ Abort Mode + .set MODE_UND, 0x1B @ Undefined Mode + .set MODE_SYS, 0x1F @ System Mode + + .equ I_BIT, 0x80 @ when I bit is set, IRQ is disabled + .equ F_BIT, 0x40 @ when F bit is set, FIQ is disabled + .equ T_BIT, 0x20 @ when T bit is set, THUMB mode is active + +start: +_start: +_mainCRTStartup: +@ +@ Clear all of memory to 0xe5e5e5e5. We use this value later to determine +@ stack highwater usage. +@ + ldr r1, .LC3 @ __data_beg__ is start of RAM + ldr r3, .LC6 @ __stack_end__ is end of RAM + sub r3, r3, r1 @ Length of RAM to set + ldr r2, =0xe5e5e5e5 @ Fill value + +.init_loop: + str r2, [r1], #4 @ Store fill value, r1 += 4 + subs r3, r3, #4 @ Length -= 4 + bgt .init_loop @ >= 0, go again + +.end_init_loop: + +@ +@ Clear BSS. +@ + ldr r1, .LC1 @ Start of memory block + ldr r3, .LC2 @ End of memory block + subs r3, r3, r1 @ Calculate length of block + beq .end_clear_loop @ If 0, nothing to do + mov r2, #0 @ Fill value + +.clear_loop: + strb r2, [r1], #1 @ Store byte, r1++ + subs r3, r3, #1 @ Decrement counter + bgt .clear_loop @ >= 0, go again + +.end_clear_loop: + +@ +@ Initialize data. +@ + ldr r1, .LC3 @ Destination (.data in RAM) + ldr r2, .LC4 @ Source (.data in FLASH) + ldr r3, .LC5 @ End of .data in RAM + subs r3, r3, r1 @ Calculate length of block + beq .end_set_loop @ If 0, nothing to do + +.set_loop: + ldrb r4, [r2], #1 @ Get byte from source, r2++ + strb r4, [r1], #1 @ Store byte to destination, r1++ + subs r3, r3, #1 @ Decrement loop counter + bgt .set_loop @ >= 0, go again + +.end_set_loop: + +@ +@ Setup a stack for each mode - note that this only sets up a usable stack +@ for system/user, SWI and IRQ modes. Also each mode is setup with +@ interrupts initially disabled. +@ + ldr r1, .LC7 @ Pointer to various values we update + ldr r0, .LC6 @ Get top of stack space + msr CPSR_c, #MODE_UND|I_BIT|F_BIT @ Undefined Instruction Mode + mov sp, r0 @ Set undef mode SP + str r0, [r1, #0] @ Store this so 'mem map' knows + + sub r0, r0, #UND_STACK_SIZE @ Subtract undef stack size for abort stack start + msr CPSR_c, #MODE_ABT|I_BIT|F_BIT @ Abort Mode + mov sp, r0 @ Set abort mode SP + str r0, [r1, #4] @ Store this so 'mem map' knows + str r0, [r1, #8] @ Store this so 'mem map' knows + + sub r0, r0, #ABT_STACK_SIZE @ Subtract abort stack size for FIQ stack start + msr CPSR_c, #MODE_FIQ|I_BIT|F_BIT @ FIQ Mode + mov sp, r0 @ Set FIQ mode SP + str r0, [r1, #12] @ Store this so 'mem map' knows + str r0, [r1, #16] @ Store this so 'mem map' knows + + sub r0, r0, #FIQ_STACK_SIZE @ Subtract FIQ stack size for IRQ stack start + msr CPSR_c, #MODE_IRQ|I_BIT|F_BIT @ IRQ Mode + mov sp, r0 @ Set IRQ mode SP + str r0, [r1, #20] @ Store this so 'mem map' knows + str r0, [r1, #24] @ Store this so 'mem map' knows + + sub r0, r0, #IRQ_STACK_SIZE @ Subtract IRQ stack size for SVC stack start + msr CPSR_c, #MODE_SVC|I_BIT|F_BIT @ Supervisor Mode + mov sp, r0 @ Set supervisor mode SP + str r0, [r1, #28] @ Store this so 'mem map' knows + str r0, [r1, #32] @ Store this so 'mem map' knows + sub r2, r0, #256 @ MAGIC! FreeRTOS only uses a few bytes of supervisor stack... + str r2, [r1, #48] @ ...so tell _sbrk() where heap ends when FreeRTOS running + + sub r0, r0, #SVC_STACK_SIZE @ Subtract supervisor stack size for system/user stack start + msr CPSR_c, #MODE_SYS|I_BIT|F_BIT @ System Mode + mov sp, r0 @ Set system/user mode SP + str r0, [r1, #36] @ Store this so 'mem map' knows + str r0, [r1, #40] @ Store this so 'mem map' knows + +@ +@ We want to start in supervisor mode (probably always, but FreeRTOS demands it) +@ + msr CPSR_c, #MODE_SVC|I_BIT|F_BIT + +@ +@ Set argc & argv, initialize newlib, and jump to main +@ + mov r0, #0 @ No arguments + mov r1, #0 @ No argv either + + bl syscallsInit @ Initialize ./newlib/syscalls.c + bl main @ And call good ol' main() + b . @ In case main() ever returns + +@ +@ Indirect words +@ + .align 0 +.LC1: .word __bss_beg__ +.LC2: .word __bss_end__ +.LC3: .word __data_beg__ +.LC4: .word __data_beg_src__ +.LC5: .word __data_end__ +.LC6: .word __stack_end__ +.LC7: .word __stack_beg_und + +@ +@ Setup vector table. +@ +.section .startup,"ax" + .code 32 + .align 0 + + b _start @ reset - _start + ldr pc, _undf @ undefined - _undf + ldr pc, _swi @ SWI - _swi + ldr pc, _pabt @ program abort - _pabt + ldr pc, _dabt @ data abort - _dabt + nop @ reserved + ldr pc, [pc, #-0xff0] @ IRQ - read the VIC + ldr pc, _fiq @ FIQ - _fiq + +_undf: .word __undf @ undefined +_swi: .word swiDispatch @ SWI +_pabt: .word __pabt @ program abort +_dabt: .word __dabt @ data abort +_fiq: .word fiqISR @ FIQ + +@ +@ Handlers for undef, program abort and data abort. They all update +@ their respective registers, then reset the system by timing out +@ the watchdog (only apparent way to force a hardware reset) +@ +__undf: + ldr sp, =(__abort_mem+5*4) @ Set sp_abt to data array with offset (restore later) + stmia sp, {r0-r12} @ Save first dataset in r0-r12 to array + sub r0, lr, #4 @ Calculate PC value of undef instruction + mov r1, #0 @ Abort type + b .abtstore @ Save info, reset system + +__pabt: + ldr sp, =(__abort_mem+5*4) @ Set sp_abt to data array with offset (restore later) + stmia sp, {r0-r12} @ Save first dataset in r0-r12 to array + sub r0, lr, #4 @ Calculate PC value of undef instruction + mov r1, #1 @ Abort type + b .abtstore @ Save info, reset system + +__dabt: + ldr sp, =(__abort_mem+5*4) @ Set sp_abt to data array with offset (restore later) + stmia sp, {r0-r12} @ Save first dataset in r0-r12 to array + sub r0, lr, #8 @ Calculate PC value of undef instruction + mov r1, #2 @ Abort type + b .abtstore @ Save info, reset system + +@ +@ Store the abort type. Then see if the sigil value is set, and if not, +@ reset the abort counter to 0. +@ +.abtstore: + ldr r2, =__abort_typ @ Abort type + str r1, [r2] @ Store it + + ldr r2, =__abort_sig @ Get the sigil address + ldr r4, =ABORT_SIGIL @ Load sigil value + ldr r3, [r2] @ Get sigil contents + cmp r3, r4 @ Sigil set? + + strne r4, [r2] @ No, store sigil value + ldrne r2, =__abort_cnt @ No, load address of abort counter + movne r4, #0 @ No, Zero for store + strne r4, [r2] @ No, Clear counter + +@ +@ Now build up structure of registers and stack (r0 = abort address, r1 = +@ abort type). This code is based heavily on the work of Roger Lynx, from +@ http://www.embedded.com/shared/printableArticle.jhtml?articleID=192202641 +@ + mrs r5, cpsr @ Save current mode to R5 for mode switching + mrs r6, spsr @ spsr_abt = CPSR of dabt originating mode, save to r6 for mode switching + mov r2, r6 @ Building second dataset: r2 = CPSR of exception + tst r6, #0x0f @ Test mode of the raised exception + orreq r6, r6, #0x0f @ If 0, elevate from user mode to system mode + msr cpsr_c, r6 @ Switch out from mode 0x17 (abort) to ... + mov r3, lr @ ... dabt generating mode and state + mov r4, sp @ ... Get lr (=r3) and sp (=r4) + msr cpsr_c, r5 @ Switch back to mode 0x17 (abort) + cmp r1, #1 @ Test for prefetch abort + moveq r1, #0 @ Can't fetch instruction at the abort address + ldrne r1, [r0] @ r1 = [pc] (dabt) + ldr sp, =__abort_mem @ Reset sp to arrays starting address + stmia sp, {r0-r4} @ Save second dataset from r0 to r4 + + ldr r1, =__abort_stk @ Space where we'll store abort stack + mov r2,#8 @ Copy 8 stack entries +.abtcopy: + ldr r0, [r4], #4 @ Get byte from source, r4 += 4 + str r0, [r1], #4 @ Store byte to destination, r1 += 4 + subs r2, r2, #1 @ Decrement loop counter + bgt .abtcopy @ >= 0, go again + + b .sysreset @ And reset + +@ +@ Force a system reset with ye olde watch dogge +@ + .set SCB_RSIR_MASK, 0x0000000f + .set SCB_RSIR, 0xe01fc180 + .set WD_MOD, 0xe0000000 + .set WD_TC, 0xe0000004 + .set WD_FEED, 0xe0000008 + .set WD_MOD_WDEN, 0x00000001 + .set WD_MOD_RESET, 0x00000002 + .set WD_MOD_TOF, 0x00000004 + .set WD_MOD_INT, 0x00000008 + .set WD_MOD_MASK, 0x0000000f + .set WD_FEED_FEED1, 0x000000aa + .set WD_FEED_FEED2, 0x00000055 + .set ABORT_SIGIL, 0xdeadc0de + +.sysreset: + ldr r1, =__abort_cnt @ Get the abort counter address + ldr r0, [r1] @ Load it + add r0, r0, #1 @ Add 1 + str r0, [r1] @ Store it back + +@ +@ Now enable the watch dog, and go into a loop waiting for a timeout +@ + ldr r0, =SCB_RSIR_MASK + ldr r1, =SCB_RSIR + str r0, [r1] + ldr r0, =WD_MOD_WDEN | WD_MOD_RESET + ldr r1, =WD_MOD + str r0, [r1] + ldr r0, =120000 + ldr r1, =WD_TC + str r0, [r1] + ldr r0, =WD_FEED_FEED1 + ldr r1, =WD_FEED + str r0, [r1] + ldr r0, =WD_FEED_FEED2 + ldr r1, =WD_FEED + str r0, [r1] + b . + +@ +@ These are in the .protected space in RAM to make sure that initialization +@ code doesn't overwrite them. When a data abort or an undefined instruction +@ exception occurs, the handlers update the respective locations below. ORDER +@ IS IMPORTANT, THESE ARE MAPPED INTO THE C STRUCTURE abortDat_t in monitor.c +@ + .global __abort_dat + .section .protected + .align 0 + +__abort_dat: .word 0 @ Dummy, not used +__abort_sig: .word 0 @ Sigil to indicate data validity +__abort_cnt: .word 0 @ Number of times we've aborted +__abort_typ: .word 0 @ Type of abort (0=undef,1=pabort,2=dabort) +__abort_mem: .space (18 * 4), 0 @ Registers from abort state +__abort_stk: .space (8 * 4), 0 @ 8 stack entries from abort state + +@ +@ Define globals so application can figure out what stacks are where. +@ Keep these in order! The stack setup code expects it. +@ + .global __stack_beg_und + .global __stack_end_und + .global __stack_beg_abt + .global __stack_end_abt + .global __stack_beg_fiq + .global __stack_end_fiq + .global __stack_beg_irq + .global __stack_end_irq + .global __stack_beg_svc + .global __stack_end_svc + .global __stack_beg_sys + .global __stack_end_sys + .global __heap_max + .global __heap_beg + .global __heap_end + .data + .align 0 + +__stack_beg_und: .word 0 @ 0 +__stack_end_und: .word 0 @ 4 +__stack_beg_abt: .word 0 @ 8 +__stack_end_abt: .word 0 @ 12 +__stack_beg_fiq: .word 0 @ 16 +__stack_end_fiq: .word 0 @ 20 +__stack_beg_irq: .word 0 @ 24 +__stack_end_irq: .word 0 @ 28 +__stack_beg_svc: .word 0 @ 32 +__stack_end_svc: .word 0 @ 36 +__stack_beg_sys: .word 0 @ 40 +__stack_end_sys: .word 0 @ 44 +__heap_max: .word 0 @ 48 +__heap_beg: .word __heap_beg__ +__heap_end: .word __heap_end__ + + .end diff --git a/cpu/Makefile b/cpu/Makefile new file mode 100644 index 0000000..98b90ca --- /dev/null +++ b/cpu/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=cpu.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/cpu/cpu.c b/cpu/cpu.c new file mode 100644 index 0000000..fc2d51f --- /dev/null +++ b/cpu/cpu.c @@ -0,0 +1,132 @@ +#include "FreeRTOS.h" +#include "cpu/cpu.h" + +// +// Olimex board specific. LEDs are on P0.10, P0.11 +// +#define partstFIRST_IO ((unsigned portLONG) 0x400) +#define partstNUM_LEDS (2) +#define partstALL_OUTPUTS_OFF ((unsigned portLONG) 0xffffffff) + +// +// +// +void cpuSetupHardware (void) +{ +#ifdef RUN_FROM_RAM + // + // Remap the interrupt vectors to RAM if we are are running from RAM + // + SCB_MEMMAP = SCB_MEMMAP_URM; +#endif + + // + // Configure the RS2332 pins. All other pins remain at their default of 0 + // + PCB_PINSEL0 |= (PCB_PINSEL0_P00_TXD0 | PCB_PINSEL0_P01_RXD0 | PCB_PINSEL0_P08_TXD1 | PCB_PINSEL0_P09_RXD1); + + // + // Set all GPIO to output other than the P0.14 (BSL), and the JTAG pins. + // The JTAG pins are left as input as I'm not sure what will happen if the + // Wiggler is connected after powerup - not that it would be a good idea to + // do that anyway. + // + GPIO0_IODIR = ~(GPIO_IO_P14 | GPIO_IO_P15 | GPIO_IO_P15); + GPIO1_IODIR = ~GPIO_IO_JTAG; + + // + // Setup the PLL to multiply the 12Mhz XTAL input by 4, divide by 1 + // + SCB_PLLCFG = (SCB_PLLCFG_MUL4 | SCB_PLLCFG_DIV1); + + // + // Activate the PLL by turning it on then feeding the correct sequence of bytes + // + SCB_PLLCON = SCB_PLLCON_PLLE; + SCB_PLLFEED = SCB_PLLFEED_FEED1; + SCB_PLLFEED = SCB_PLLFEED_FEED2; + + // + // Wait for the PLL to lock... + // + while (!(SCB_PLLSTAT & SCB_PLLSTAT_PLOCK)) + ; + + // + // ...before connecting it using the feed sequence again + // + SCB_PLLCON = SCB_PLLCON_PLLC | SCB_PLLCON_PLLE; + SCB_PLLFEED = SCB_PLLFEED_FEED1; + SCB_PLLFEED = SCB_PLLFEED_FEED2; + + // + // Setup and turn on the MAM. Three cycle access is used due to the fast + // PLL used. It is possible faster overall performance could be obtained by + // tuning the MAM and PLL settings. + // + MAM_TIM = MAM_TIM_3; + MAM_CR = MAM_CR_FULL; + + // + // Setup the peripheral bus to be the same as the PLL output (48Mhz) + // + SCB_VPBDIV = SCB_VPBDIV_100; + + // + // Disable power to all modules + // + SCB_PCONP = 0x0000; + + // + // + // + cpuGPIOInitialize (); +} + +// +// +// +void cpuPLLDisable (void) +{ + SCB_PLLCON = 0; + SCB_PLLFEED = SCB_PLLFEED_FEED1; + SCB_PLLFEED = SCB_PLLFEED_FEED2; + SCB_PLLCFG = 0; +} + +// +// +// +void cpuT1Disable (void) +{ + T1_TCR = 0; + T1_PR = 0; + T1_MCR = 0; + T1_CCR = 0; + T1_EMR = 0; + T1_CTCR = 0; + T1_IR = 0; +} + +// +// +// +void cpuGPIOInitialize (void) +{ + GPIO0_IOSET = partstALL_OUTPUTS_OFF; +} + +void cpuToggleLED (unsigned portBASE_TYPE uxLED) +{ + unsigned portLONG ulLED = partstFIRST_IO; + + if (uxLED < partstNUM_LEDS) + { + ulLED <<= (unsigned portLONG) uxLED; + + if (GPIO0_IOPIN & ulLED) + GPIO0_IOCLR = ulLED; + else + GPIO0_IOSET = ulLED; + } +} diff --git a/cpu/cpu.h b/cpu/cpu.h new file mode 100644 index 0000000..ff086b0 --- /dev/null +++ b/cpu/cpu.h @@ -0,0 +1,10 @@ +#ifndef _CPU_H_ +#define _CPU_H_ + +void cpuSetupHardware (void); +void cpuPLLDisable (void); +void cpuT1Disable (void); +void cpuGPIOInitialize (void); +void cpuToggleLED (unsigned portBASE_TYPE uxLED); + +#endif diff --git a/dac/Makefile b/dac/Makefile new file mode 100644 index 0000000..b41b0cc --- /dev/null +++ b/dac/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=dac.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/dac/dac.c b/dac/dac.c new file mode 100644 index 0000000..f981624 --- /dev/null +++ b/dac/dac.c @@ -0,0 +1,26 @@ +#include "FreeRTOS.h" + +#include "dac.h" + +// +// +// +void dacInit (void) +{ + PCB_PINSEL1 |= PCB_PINSEL1_P025_AOUT; + + DAC_CR = 0; +} + +unsigned int dacSet (unsigned int newValue) +{ + unsigned int dacCR; + unsigned int dacCurrentValue; + + dacCR = DAC_CR; + dacCurrentValue = (dacCR & DAC_CR_VALUEMASK) >> DAC_CR_VALUESHIFT; + dacCR = (dacCR & ~DAC_CR_VALUEMASK) | ((newValue << DAC_CR_VALUESHIFT) & DAC_CR_VALUEMASK); + DAC_CR = dacCR; + + return dacCurrentValue; +} diff --git a/dac/dac.h b/dac/dac.h new file mode 100644 index 0000000..cd91a9d --- /dev/null +++ b/dac/dac.h @@ -0,0 +1,7 @@ +#ifndef _DAC_H_ +#define _DAC_H_ + +void dacInit (void); +unsigned int dacSet (unsigned int newValue); + +#endif diff --git a/eints/Makefile b/eints/Makefile new file mode 100644 index 0000000..d9c4aae --- /dev/null +++ b/eints/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=eints.c eintsISR.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/eints/eints.c b/eints/eints.c new file mode 100644 index 0000000..85f9b8e --- /dev/null +++ b/eints/eints.c @@ -0,0 +1,30 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "eints.h" +#include "eintsISR.h" + +// +// +// +void eintsInit (void) +{ + portENTER_CRITICAL (); + + PCB_PINSEL1 |= PCB_PINSEL1_P016_EINT0; + PCB_PINSEL0 |= PCB_PINSEL0_P015_EINT2; + + SCB_EXTPOLAR &= ~(SCB_EXTPOLAR_EINT0 | SCB_EXTPOLAR_EINT2); + SCB_EXTMODE |= (SCB_EXTMODE_EINT0 | SCB_EXTMODE_EINT2); + SCB_EXTINT |= (SCB_EXTINT_EINT0 | SCB_EXTINT_EINT2); + + VIC_IntSelect &= ~(VIC_IntSelect_EINT0 | VIC_IntSelect_EINT2); + VIC_VectAddr4 = (portLONG) eintsISR_EINT0; + VIC_VectCntl4 = VIC_VectCntl_ENABLE | VIC_Channel_EINT0; + VIC_VectAddr5 = (portLONG) eintsISR_EINT2; + VIC_VectCntl5 = VIC_VectCntl_ENABLE | VIC_Channel_EINT2; + VIC_IntEnable = VIC_IntEnable_EINT0 | VIC_IntEnable_EINT2; + + portEXIT_CRITICAL (); +} diff --git a/eints/eints.h b/eints/eints.h new file mode 100644 index 0000000..649699c --- /dev/null +++ b/eints/eints.h @@ -0,0 +1,6 @@ +#ifndef _EINTS_H_ +#define _EINTS_H_ + +void eintsInit (void); + +#endif diff --git a/eints/eintsISR.c b/eints/eintsISR.c new file mode 100644 index 0000000..e801d58 --- /dev/null +++ b/eints/eintsISR.c @@ -0,0 +1,42 @@ +// +// +// +#include "FreeRTOS.h" +#include "task.h" + +#include "eintsISR.h" + +// +// +// +void eintsISR_EINT0 (void) __attribute__ ((naked)); +void eintsISR_EINT2 (void) __attribute__ ((naked)); + +// +// +// +void eintsISR_EINT0 (void) +{ + portENTER_SWITCHING_ISR (); + + SCB_EXTINT |= SCB_EXTINT_EINT0; + + GPIO0_IOSET = GPIO_IO_P11; + + VIC_VectAddr = (unsigned portLONG) 0; + + portEXIT_SWITCHING_ISR (0); +} + +void eintsISR_EINT2 (void) +{ + portENTER_SWITCHING_ISR (); + + SCB_EXTINT |= SCB_EXTINT_EINT1; + + GPIO0_IOCLR = GPIO_IO_P11; + + VIC_VectAddr = (unsigned portLONG) 0; + + portEXIT_SWITCHING_ISR (0); +} diff --git a/eints/eintsISR.h b/eints/eintsISR.h new file mode 100644 index 0000000..fdfbed1 --- /dev/null +++ b/eints/eintsISR.h @@ -0,0 +1,7 @@ +#ifndef _EINTSISR_H_ +#define _EINTSISR_H_ + +void eintsISR_EINT0 (void); +void eintsISR_EINT2 (void); + +#endif diff --git a/fatfs/Makefile b/fatfs/Makefile new file mode 100644 index 0000000..2a5432e --- /dev/null +++ b/fatfs/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=ff.c mmc.c spi.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/fatfs/disk.h b/fatfs/disk.h new file mode 100644 index 0000000..013ef6b --- /dev/null +++ b/fatfs/disk.h @@ -0,0 +1,73 @@ +/************************************************************************* + * + * Used with ICCARM and AARM. + * + * (c) Copyright IAR Systems 2005 + * + * File name : disk.h + * Description : Disk common definitions module + * + * History : + * 1. Data : November 15, 2005 + * Author : Stanimir Bonev + * Description : Create + * + * $Revision: 1.4 $ +**************************************************************************/ +#ifndef _DISK_H_ +#define _DISK_H_ + +#include "sysdefs.h" + +typedef enum +{ + DiskCommandPass = 0, + DiskNotReady, + DiskNotPresent, + DiskParametersError, + DiskMiscompareError, + DiskChanged, + DiskUknowError, +} +DiskStatusCode_t; + +typedef enum +{ + DiskWrite = 0, + DiskRead, + DiskVerify, +} +DiskIoRequest_t; + +typedef enum +{ + DiskInquiry = 0, + DiskFormatCapacity, +} +DiskInfoType_t; + +typedef enum +{ + DiskMMC, + DiskSD, + DiskUnknow, +} +DiskType_t; + +typedef struct +{ + U32 BlockNumb; + U32 BlockSize; + DiskStatusCode_t DiskStatus; + DiskType_t DiskType; + BOOL WriteProtect; + BOOL MediaChanged; +} +/* __attribute__ ((packed)) */ DiskStatus_t, *pDiskStatus_t; + +typedef void (* DiskInitFpnt_t) (void); +typedef U32 (* DiskInfoFpnt_t) (U8 *, DiskInfoType_t); +typedef pDiskStatus_t (* DiskStatusFpnt_t) (void); +typedef DiskStatusCode_t (* DiskIoFpnt_t) (U8 *, U32, U32, DiskIoRequest_t); + +#endif diff --git a/fatfs/diskio.h b/fatfs/diskio.h new file mode 100644 index 0000000..610a4e2 --- /dev/null +++ b/fatfs/diskio.h @@ -0,0 +1,61 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.04a (C)ChaN, 2007 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_H_ +#define _DISKIO_H_ + +// +// Status of Disk Functions +// +typedef U8 DSTATUS; + +// +// Results of Disk Functions +// +typedef enum +{ + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} +DRESULT; + +// +// Disk Status Bits (DSTATUS) +// +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + +// +// Command code for disk_ioctl() +// +#define GET_SECTOR_COUNT 1 +#define GET_SECTOR_SIZE 2 +#define CTRL_SYNC 3 +#define CTRL_POWER 4 +#define CTRL_LOCK 5 +#define CTRL_EJECT 6 +#define MMC_GET_CSD 10 +#define MMC_GET_CID 11 +#define MMC_GET_OCR 12 +#define ATA_GET_REV 20 +#define ATA_GET_MODEL 21 +#define ATA_GET_SN 22 + +// +// +// +DSTATUS diskInitialize (U8); +DSTATUS diskShutdown (void); +DSTATUS diskStatus (U8); +DRESULT diskRead (U8, U8 *, U32, U8); +#if _FS_READONLY == 0 +DRESULT diskWrite (U8, const U8 *, U32, U8); +#endif +DRESULT diskIoctl (U8, U8, void *); + +#endif diff --git a/fatfs/ff.c b/fatfs/ff.c new file mode 100644 index 0000000..9d98d53 --- /dev/null +++ b/fatfs/ff.c @@ -0,0 +1,1841 @@ +/*--------------------------------------------------------------------------/ + / FatFs - FAT file system module R0.04b (C)ChaN, 2007 + /---------------------------------------------------------------------------/ + / The FatFs module is an experimenal project to implement FAT file system to + / cheap microcontrollers. This is a free software and is opened for education, + / research and development under license policy of following trems. + / + / Copyright (C) 2007, ChaN, all right reserved. + / + / * The FatFs module is a free software and there is no warranty. + / * You can use, modify and/or redistribute it for personal, non-profit or + / profit use without any restriction under your responsibility. + / * Redistributions of source code must retain the above copyright notice. + / + /---------------------------------------------------------------------------/ + / Feb 26, 2006 R0.00 Prototype. + / Apr 29, 2006 R0.01 First stable version. + / Jun 01, 2006 R0.02 Added FAT12 support. + / Removed unbuffered mode. + / Fixed a problem on small (<32M) patition. + / Jun 10, 2006 R0.02a Added a configuration option (_FS_MINIMUM). + / Sep 22, 2006 R0.03 Added f_rename(). + / Changed option _FS_MINIMUM to _FS_MINIMIZE. + / Dec 11, 2006 R0.03a Improved cluster scan algolithm to write files fast. + / Fixed f_mkdir() creates incorrect directory on FAT32. + / Feb 04, 2007 R0.04 Supported multiple drive system. + / Changed some interfaces for multiple drive system. + / Changed f_mountdrv() to f_mount(). + / Added f_mkfs(). + / Apr 01, 2007 R0.04a Supported multiple partitions on a plysical drive. + / Added a capability of extending file size to f_lseek(). + / Added minimization level 3. + / Fixed an endian sensitive code in f_mkfs(). + / May 05, 2007 R0.04b Added a configuration option _USE_NTFLAG. + / Added FSInfo support. + / Fixed DBCS name can result FR_INVALID_NAME. + / Fixed short seek (<= csize) collapses the file object. + /---------------------------------------------------------------------------*/ +#include // ### +#include +#include "ff.h" /* FatFs declarations */ +#include "diskio.h" /* Include file for user provided disk functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Functions + + ---------------------------------------------------------------------------*/ + +static FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ +static U16 fsid; /* File system mount ID */ + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +BOOL move_window ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + U32 sector /* Sector number to make apperance in the fs->win[] */ + ) /* Move to zero only writes back dirty window */ +{ + U32 wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if _FS_READONLY == 0 + U8 n; + if (fs->winflag) { /* Write back dirty window if needed */ + if (diskWrite(fs->drive, fs->win, wsect, 1) != RES_OK) + return FALSE; + fs->winflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to FAT copy */ + wsect += fs->sects_fat; + diskWrite(fs->drive, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (diskRead(fs->drive, fs->win, sector, 1) != RES_OK) + return FALSE; + fs->winsect = sector; + } + } + return TRUE; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ + +#if _FS_READONLY == 0 +static +FRESULT sync ( /* FR_OK: successful, FR_RW_ERROR: failed */ + FATFS *fs /* File system object */ + ) +{ + fs->winflag = 1; + if (!move_window(fs, 0)) return FR_RW_ERROR; +#if _USE_FSINFO + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { /* Update FSInfo sector if needed */ + fs->winsect = 0; + memset(fs->win, 0, 512); + ST_U16(&fs->win[BS_55AA], 0xAA55); + ST_U32(&fs->win[FSI_LeadSig], 0x41615252); + ST_U32(&fs->win[FSI_StrucSig], 0x61417272); + ST_U32(&fs->win[FSI_Free_Count], fs->free_clust); + ST_U32(&fs->win[FSI_Nxt_Free], fs->last_clust); + diskWrite(0, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } +#endif + if (diskIoctl(fs->drive, CTRL_SYNC, NULL) != RES_OK) return FR_RW_ERROR; + return FR_OK; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get a cluster status */ +/*-----------------------------------------------------------------------*/ + +static +U32 get_cluster ( /* 0,>=2: successful, 1: failed */ + FATFS *fs, /* File system object */ + U32 clust /* Cluster# to get the link information */ + ) +{ + U16 wc, bc; + U32 fatsect; + + + if (clust >= 2 && clust < fs->max_clust) { /* Valid cluster# */ + fatsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (U16)clust * 3 / 2; + if (!move_window(fs, fatsect + (bc / S_SIZ))) break; + wc = fs->win[bc & (S_SIZ - 1)]; bc++; + if (!move_window(fs, fatsect + (bc / S_SIZ))) break; + wc |= (U16)fs->win[bc & (S_SIZ - 1)] << 8; + return (clust & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (!move_window(fs, fatsect + (clust / (S_SIZ / 2)))) break; + return LD_U16(&fs->win[((U16)clust * 2) & (S_SIZ - 1)]); + + case FS_FAT32 : + if (!move_window(fs, fatsect + (clust / (S_SIZ / 4)))) break; + return LD_U32(&fs->win[((U16)clust * 4) & (S_SIZ - 1)]) & 0x0FFFFFFF; + } + } + + return 1; /* There is no cluster information, or an error occured */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change a cluster status */ +/*-----------------------------------------------------------------------*/ + +#if _FS_READONLY == 0 +static +BOOL put_cluster ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + U32 clust, /* Cluster# to change */ + U32 val /* New value to mark the cluster */ + ) +{ + U16 bc; + U8 *p; + U32 fatsect; + + + fatsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (U16)clust * 3 / 2; + if (!move_window(fs, fatsect + (bc / S_SIZ))) return FALSE; + p = &fs->win[bc & (S_SIZ - 1)]; + *p = (clust & 1) ? ((*p & 0x0F) | ((U8)val << 4)) : (U8)val; + bc++; + fs->winflag = 1; + if (!move_window(fs, fatsect + (bc / S_SIZ))) return FALSE; + p = &fs->win[bc & (S_SIZ - 1)]; + *p = (clust & 1) ? (U8)(val >> 4) : ((*p & 0xF0) | ((U8)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + if (!move_window(fs, fatsect + (clust / (S_SIZ / 2)))) return FALSE; + ST_U16(&fs->win[((U16)clust * 2) & (S_SIZ - 1)], (U16)val); + break; + + case FS_FAT32 : + if (!move_window(fs, fatsect + (clust / (S_SIZ / 4)))) return FALSE; + ST_U32(&fs->win[((U16)clust * 4) & (S_SIZ - 1)], val); + break; + + default : + return FALSE; + } + fs->winflag = 1; + return TRUE; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ + +#if _FS_READONLY == 0 +static +BOOL remove_chain ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + U32 clust /* Cluster# to remove chain from */ + ) +{ + U32 nxt; + + + while (clust >= 2 && clust < fs->max_clust) { + nxt = get_cluster(fs, clust); + if (nxt == 1) return FALSE; + if (!put_cluster(fs, clust, 0)) return FALSE; + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust++; +#if _USE_FSINFO + fs->fsi_flag = 1; +#endif + } + clust = nxt; + } + return TRUE; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Stretch or create a cluster chain */ +/*-----------------------------------------------------------------------*/ + +#if _FS_READONLY == 0 +static +U32 create_chain ( /* 0: no free cluster, 1: error, >=2: new cluster number */ + FATFS *fs, /* File system object */ + U32 clust /* Cluster# to stretch, 0 means create new */ + ) +{ + U32 cstat, ncl, scl, mcl = fs->max_clust; + + + if (clust == 0) { /* Create new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (scl == 0 || scl >= mcl) scl = 1; + } + else { /* Stretch existing chain */ + cstat = get_cluster(fs, clust); /* Check the cluster status */ + if (cstat < 2) return 1; /* It is an invalid cluster */ + if (cstat < mcl) return cstat; /* It is already followed by next cluster */ + scl = clust; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cstat = get_cluster(fs, ncl); /* Get the cluster status */ + if (cstat == 0) break; /* Found a free cluster */ + if (cstat == 1) return 1; /* Any error occured */ + if (ncl == scl) return 0; /* No free custer */ + } + + if (!put_cluster(fs, ncl, 0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */ + if (clust && !put_cluster(fs, clust, ncl)) return 1; /* Link it to previous one if needed */ + + fs->last_clust = ncl; /* Update fsinfo */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; +#if _USE_FSINFO + fs->fsi_flag = 1; +#endif + } + + return ncl; /* Return new cluster number */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +U32 clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */ + FATFS *fs, /* File system object */ + U32 clust /* Cluster# to be converted */ + ) +{ + clust -= 2; + if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clust * fs->sects_clust + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Move directory pointer to next */ +/*-----------------------------------------------------------------------*/ + +static +BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */ + DIR *dirobj /* Pointer to directory object */ + ) +{ + U32 clust; + U16 idx; + FATFS *fs = dirobj->fs; + + + idx = dirobj->index + 1; + if ((idx & ((S_SIZ - 1) / 32)) == 0) { /* Table sector changed? */ + dirobj->sect++; /* Next sector */ + if (!dirobj->clust) { /* In static table */ + if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */ + } else { /* In dynamic table */ + if (((idx / (S_SIZ / 32)) & (fs->sects_clust - 1)) == 0) { /* Cluster changed? */ + clust = get_cluster(fs, dirobj->clust); /* Get next cluster */ + if (clust < 2 || clust >= fs->max_clust) /* Reached to end of table */ + return FALSE; + dirobj->clust = clust; /* Initialize for new cluster */ + dirobj->sect = clust2sect(fs, clust); + } + } + } + dirobj->index = idx; /* Lower 4 bit of dirobj->index indicates offset in dirobj->sect */ + return TRUE; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file status from directory entry */ +/*-----------------------------------------------------------------------*/ + +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + FILINFO *finfo, /* Ptr to store the file information */ + const U8 *dir /* Ptr to the directory entry */ + ) +{ + U8 n, c, a; + char *p; + + + p = &finfo->fname[0]; + a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */ + for (n = 0; n < 8; n++) { /* Convert file name (body) */ + c = dir[n]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Convert file name (extension) */ + *p++ = '.'; + for (n = 8; n < 11; n++) { + c = dir[n]; + if (c == ' ') break; + if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20; + *p++ = c; + } + } + *p = '\0'; + + finfo->fattrib = dir[DIR_Attr]; /* Attribute */ + finfo->fsize = LD_U32(&dir[DIR_FileSize]); /* Size */ + finfo->fdate = LD_U16(&dir[DIR_WrtDate]); /* Date */ + finfo->ftime = LD_U16(&dir[DIR_WrtTime]); /* Time */ +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a paragraph and create the name in format of directory entry */ +/*-----------------------------------------------------------------------*/ + +static +char make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */ + const char **path, /* Pointer to the file path pointer */ + char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */ + ) +{ + U8 n, t, c, a, b; + + memset(dirname, ' ', 8+3); /* Fill buffer with spaces */ + a = 0; b = 0x18; /* NT flag */ + n = 0; t = 8; + + for (;;) { + + c = *(*path)++; + + if (c == '\0' || c == '/') { /* Reached to end of str or directory separator */ + if (n == 0) break; + dirname[11] = _USE_NTFLAG ? (a & b) : 0; + return c; + } + if (c <= ' ' || c == 0x7F) break; /* Reject invisible chars */ + if (c == '.') { + if (!(a & 1) && n >= 1 && n <= 8) { /* Enter extension part */ + n = 8; t = 11; continue; + } + break; + } + if (_USE_SJIS && + ((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code */ + (c >= 0xE0 && c <= 0xFC))) { + if (n == 0 && c == 0xE5) /* Change heading \xE5 to \x05 */ + c = 0x05; + a ^= 1; goto md_l2; + } + if (c == '"') break; /* Reject " */ + if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */ + if (c <= ',') break; /* Reject * + , */ + if (c <= '9') goto md_l1; /* Accept - 0-9 */ + if (c <= '?') break; /* Reject : ; < = > ? */ + if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */ + if (c == '|') break; /* Reject | */ + if (c >= '[' && c <= ']') break;/* Reject [ \ ] */ + if (_USE_NTFLAG && c >= 'A' && c <= 'Z') + (t == 8) ? (b &= ~0x08) : (b &= ~0x10); + if (c >= 'a' && c <= 'z') { /* Convert to upper case */ + c -= 0x20; + if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10); + } + } +md_l1: + a &= ~1; +md_l2: + if (n >= t) break; + dirname[n++] = c; + } + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Trace a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dirobj, /* Pointer to directory object to return last directory */ + char *fn, /* Pointer to last segment name to return {file(8),ext(3),attr(1)} */ + const char *path, /* Full-path string to trace a file or directory */ + U8 **dir /* Directory pointer in Win[] to retutn */ + ) +{ + U32 clust; + char ds; + U8 *dptr = NULL; + FATFS *fs = dirobj->fs; /* Get logical drive from the given DIR structure */ + + + /* Initialize directory object */ + clust = fs->dirbase; + if (fs->fs_type == FS_FAT32) { + dirobj->clust = dirobj->sclust = clust; + dirobj->sect = clust2sect(fs, clust); + } else { + dirobj->clust = dirobj->sclust = 0; + dirobj->sect = clust; + } + dirobj->index = 0; + + if (*path == '\0') { /* Null path means the root directory */ + *dir = NULL; return FR_OK; + } + + for (;;) { + ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */ + if (ds == 1) return FR_INVALID_NAME; + for (;;) { + if (!move_window(fs, dirobj->sect)) return FR_RW_ERROR; + dptr = &fs->win[(dirobj->index & ((S_SIZ - 1) / 32)) * 32]; /* Pointer to the directory entry */ + if (dptr[DIR_Name] == 0) /* Has it reached to end of dir? */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + if (dptr[DIR_Name] != 0xE5 /* Matched? */ + && !(dptr[DIR_Attr] & AM_VOL) + && !memcmp(&dptr[DIR_Name], fn, 8+3) ) break; + if (!next_dir_entry(dirobj)) /* Next directory pointer */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + } + if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */ + if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */ + clust = ((U32)LD_U16(&dptr[DIR_FstClusHI]) << 16) | LD_U16(&dptr[DIR_FstClusLO]); /* Get cluster# of the directory */ + dirobj->clust = dirobj->sclust = clust; /* Restart scanning at the new directory */ + dirobj->sect = clust2sect(fs, clust); + dirobj->index = 2; + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* Reserve a directory entry */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */ + DIR *dirobj, /* Target directory to create new entry */ + U8 **dir /* Pointer to pointer to created entry to retutn */ + ) +{ + U32 clust, sector; + U8 c, n, *dptr; + FATFS *fs = dirobj->fs; + + + /* Re-initialize directory object */ + clust = dirobj->sclust; + if (clust) { /* Dyanmic directory table */ + dirobj->clust = clust; + dirobj->sect = clust2sect(fs, clust); + } else { /* Static directory table */ + dirobj->sect = fs->dirbase; + } + dirobj->index = 0; + + do { + if (!move_window(fs, dirobj->sect)) return FR_RW_ERROR; + dptr = &fs->win[(dirobj->index & ((S_SIZ - 1) / 32)) * 32]; /* Pointer to the directory entry */ + c = dptr[DIR_Name]; + if (c == 0 || c == 0xE5) { /* Found an empty entry! */ + *dir = dptr; return FR_OK; + } + } while (next_dir_entry(dirobj)); /* Next directory pointer */ + /* Reached to end of the directory table */ + + /* Abort when static table or could not stretch dynamic table */ + if (!clust || !(clust = create_chain(fs, dirobj->clust))) return FR_DENIED; + if (clust == 1 || !move_window(fs, 0)) return FR_RW_ERROR; + + fs->winsect = sector = clust2sect(fs, clust); /* Cleanup the expanded table */ + memset(fs->win, 0, S_SIZ); + for (n = fs->sects_clust; n; n--) { + if (diskWrite(fs->drive, fs->win, sector, 1) != RES_OK) + return FR_RW_ERROR; + sector++; + } + fs->winflag = 1; + *dir = fs->win; + return FR_OK; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is a FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +U8 check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */ + FATFS *fs, /* File system object */ + U32 sect /* Sector# (lba) to check if it is a FAT boot record or not */ + ) +{ + if (diskRead(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 2; + if (LD_U16(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always offset 510) */ + return 2; + + if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */ + return 0; + if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80)) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ + const char **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + U8 chk_wp /* !=0: Check media write protection for wrinting fuctions */ + ) +{ + U8 drv, fmt, *tbl; + DSTATUS stat; + U32 bootsect, fatsize, totalsect, maxclust; + const char *p = *path; + FATFS *fs; + + + /* Get drive number from the path name */ + while (*p == ' ') p++; /* Strip leading spaces */ + drv = p[0] - '0'; /* Is there a drive number? */ + if (drv <= 9 && p[1] == ':') + p += 2; /* Found a drive number, get and strip it */ + else + drv = 0; /* No drive number is given, select drive 0 in default */ + if (*p == '/') p++; /* Strip heading slash */ + *path = p; /* Return pointer to the path name */ + + /* Check if the drive number is valid or not */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */ + if (!(fs = FatFs[drv])) return FR_NOT_ENABLED; /* Is the file system object registered? */ + *rfs = fs; /* Returen pointer to the corresponding file system object */ + + /* Check if the logical drive has been mounted or not */ + if (fs->fs_type) { + stat = diskStatus(fs->drive); + if (!(stat & STA_NOINIT)) { /* If the physical drive is kept initialized */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive has not been mounted, following code attempts to mount the logical drive */ + + memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */ + fs->drive = LD2PD(drv); /* Bind the logical drive and a physical drive */ + stat = diskInitialize (fs->drive); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the drive is ready */ + return FR_NOT_READY; +#if S_MAX_SIZ > 512 /* Check disk sector size */ + if (diskIoctl(drv, GET_SECTOR_SIZE, &S_SIZ) != RES_OK || S_SIZ > S_MAX_SIZ) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bootsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not a FAT boot record, it may be patitioned */ + /* Check a partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(drv) * 16]; /* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bootsect = LD_U32(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bootsect); /* Check the partition */ + } + } + if (fmt || LD_U16(&fs->win[BPB_BytsPerSec]) != S_SIZ) /* No valid FAT patition is found */ + return FR_NO_FILESYSTEM; + + /* Initialize the file system object */ + fatsize = LD_U16(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT */ + if (!fatsize) fatsize = LD_U32(&fs->win[BPB_FATSz32]); + fs->sects_fat = fatsize; + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bootsect + LD_U16(&fs->win[BPB_RsvdSecCnt]); /* FAT start sector (lba) */ + fs->sects_clust = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_U16(&fs->win[BPB_RootEntCnt]); /* Nmuber of root directory entries */ + totalsect = LD_U16(&fs->win[BPB_TotSec16]); /* Number of sectors on the file system */ + if (!totalsect) totalsect = LD_U32(&fs->win[BPB_TotSec32]); + fs->max_clust = maxclust = (totalsect /* Last cluster# + 1 */ + - LD_U16(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / (S_SIZ/32) + ) / fs->sects_clust + 2; + + fmt = FS_FAT12; /* Determine the FAT sub type */ + if (maxclust > 0xFF7) fmt = FS_FAT16; + if (maxclust > 0xFFF7) fmt = FS_FAT32; + fs->fs_type = fmt; + + if (fmt == FS_FAT32) + fs->dirbase = LD_U32(&fs->win[BPB_RootClus]); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fatsize + fs->n_rootdir / (S_SIZ/32); /* Data start sector (lba) */ + +#if !_FS_READONLY + fs->free_clust = 0xFFFFFFFF; +#if _USE_FSINFO + /* Load fsinfo sector if needed */ + if (fmt == FS_FAT32) { + fs->fsi_sector = bootsect + LD_U16(&fs->win[BPB_FSInfo]); + if (diskRead(0, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_U16(&fs->win[BS_55AA]) == 0xAA55 && + LD_U32(&fs->win[FSI_LeadSig]) == 0x41615252 && + LD_U32(&fs->win[FSI_StrucSig]) == 0x61417272) { + fs->last_clust = LD_U32(&fs->win[FSI_Nxt_Free]); + fs->free_clust = LD_U32(&fs->win[FSI_Free_Count]); + } + } +#endif +#endif + fs->id = ++fsid; /* File system mount ID */ + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Not valid */ + const FATFS *fs, /* Pointer to the file system object */ + U16 id /* id member of the target object to be checked */ + ) +{ + if (!fs || fs->id != id) + return FR_INVALID_OBJECT; + if (diskStatus(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + + --------------------------------------------------------------------------*/ + + +void f_printerror (FRESULT f) +{ + unsigned int i; + + typedef struct errorStrings_s + { + FRESULT fresult; + const char *string; + } + errorStrings_t; + + static errorStrings_t errorStrings [] = + { + { FR_OK, "OK" }, + { FR_NOT_READY, "NOT_READY" }, + { FR_NO_FILE, "NO_FILE" }, + { FR_NO_PATH, "NO_PATH" }, + { FR_INVALID_NAME, "INVALID_NAME" }, + { FR_INVALID_DRIVE, "INVALID_DRIVE" }, + { FR_DENIED, "DENIED" }, + { FR_EXIST, "EXIST" }, + { FR_RW_ERROR, "RW_ERROR" }, + { FR_WRITE_PROTECTED, "WRITE_PROTECTED" }, + { FR_NOT_ENABLED, "NOT_ENABLED" }, + { FR_NO_FILESYSTEM, "NO_FILESYSTEM" }, + { FR_INVALID_OBJECT, "INVALID_OBJECT" }, + { FR_MKFS_ABORTED, "MKFS_ABORTED" }, + }; + + for (i = 0; i < arrsizeof (errorStrings); i++) + { + if (errorStrings [i].fresult == f) + { + printf ("rrc=%u FR_%s\n", f, errorStrings [f].string); + return; + } + } + + printf ("rrc=%u (no text equivalent)\n", f); +} + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount (U8 drv, FATFS *fs) +{ + FATFS *fsobj; + + if (drv >= _DRIVES) + return FR_INVALID_DRIVE; + + fsobj = FatFs [drv]; + FatFs [drv] = fs; + + if (fsobj) + memset (fsobj, 0, sizeof (FATFS)); + if (fs) + memset (fs, 0, sizeof (FATFS)); + + return FR_OK; +} + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const char *path, /* Pointer to the file name */ + U8 mode /* Access mode and file open mode flags */ + ) +{ + FRESULT res; + U8 *dir; + DIR dirobj; + char fn[8+3+1]; + FATFS *fs; + + + fp->fs = NULL; +#if !_FS_READONLY + mode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW); + res = auto_mount(&path, &fs, (U8)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = auto_mount(&path, &fs, 0); +#endif + if (res != FR_OK) return res; + dirobj.fs = fs; + + /* Trace the file path */ + res = trace_path(&dirobj, fn, path, &dir); +#if !_FS_READONLY + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) { + U32 ps, rs; + if (res != FR_OK) { /* No file, create new */ + if (res != FR_NO_FILE) return res; + res = reserve_direntry(&dirobj, &dir); + if (res != FR_OK) return res; + memset(dir, 0, 32); /* Initialize the new entry with open name */ + memcpy(&dir[DIR_Name], fn, 8+3); + dir[DIR_NTres] = fn[11]; + mode |= FA_CREATE_ALWAYS; + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + return FR_EXIST; + if (dir == NULL || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + return FR_DENIED; + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero if needed */ + rs = ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]); /* Get start cluster */ + ST_U16(&dir[DIR_FstClusHI], 0); /* cluster = 0 */ + ST_U16(&dir[DIR_FstClusLO], 0); + ST_U32(&dir[DIR_FileSize], 0); /* size = 0 */ + fs->winflag = 1; + ps = fs->winsect; /* Remove the cluster chain */ + if (!remove_chain(fs, rs) || !move_window(fs, ps)) + return FR_RW_ERROR; + fs->last_clust = rs - 1; /* Reuse the cluster hole */ + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = AM_ARC; /* New attribute */ + ps = get_fattime(); + ST_U32(&dir[DIR_WrtTime], ps); /* Updated time */ + ST_U32(&dir[DIR_CrtTime], ps); /* Created time */ + fs->winflag = 1; + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) return res; /* Trace failed */ + if (dir == NULL || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + return FR_NO_FILE; +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + return FR_DENIED; + } + + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#endif + fp->flag = mode; /* File access mode */ + fp->org_clust = /* File start cluster */ + ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]); + fp->fsize = LD_U32(&dir[DIR_FileSize]); /* File size */ + fp->fptr = 0; /* File ptr */ + fp->sect_clust = 1; /* Sector counter */ + fp->fs = fs; fp->id = fs->id; /* Owner file system object of the file */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + U16 btr, /* Number of bytes to read */ + U16 *br /* Pointer to number of bytes read */ + ) +{ + U32 clust, sect, remain; + U16 rcnt; + U8 cc, *rbuff = buff; + FRESULT res; + FATFS *fs = fp->fs; + + + *br = 0; + res = validate(fs, fp->id); /* Check validity of the object */ + if (res) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */ + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (U16)remain; /* Truncate read count by number of bytes left */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr & (S_SIZ - 1)) == 0) { /* On the sector boundary */ + if (--fp->sect_clust) { /* Decrement left sector counter */ + sect = fp->curr_sect + 1; /* Get current sector */ + } else { /* On the cluster boundary, get next cluster */ + clust = (fp->fptr == 0) ? + fp->org_clust : get_cluster(fs, fp->curr_clust); + if (clust < 2 || clust >= fs->max_clust) + goto fr_error; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(fs, clust); /* Get current sector */ + fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */ + } +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Flush file I/O buffer if needed */ + if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fr_error; + fp->flag &= ~FA__DIRTY; + } +#endif + fp->curr_sect = sect; /* Update current sector */ + cc = btr / S_SIZ; /* When left bytes >= S_SIZ, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (cc > fp->sect_clust) cc = fp->sect_clust; + if (diskRead(fs->drive, rbuff, sect, cc) != RES_OK) + goto fr_error; + fp->sect_clust -= cc - 1; + fp->curr_sect += cc - 1; + rcnt = cc * S_SIZ; continue; + } + if (diskRead(fs->drive, fp->buffer, sect, 1) != RES_OK) /* Load the sector into file I/O buffer */ + goto fr_error; + } + rcnt = S_SIZ - ((U16)fp->fptr & (S_SIZ - 1)); /* Copy fractional bytes from file I/O buffer */ + if (rcnt > btr) rcnt = btr; + memcpy(rbuff, &fp->buffer[fp->fptr & (S_SIZ - 1)], rcnt); + } + + return FR_OK; + +fr_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + U16 btw, /* Number of bytes to write */ + U16 *bw /* Pointer to number of bytes written */ + ) +{ + U32 clust, sect; + U16 wcnt; + U8 cc; + FRESULT res; + const U8 *wbuff = buff; + FATFS *fs = fp->fs; + + + *bw = 0; + res = validate(fs, fp->id); /* Check validity of the object */ + if (res) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */ + if (fp->fsize + btw < fp->fsize) return FR_OK; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr & (S_SIZ - 1)) == 0) { /* On the sector boundary */ + if (--fp->sect_clust) { /* Decrement left sector counter */ + sect = fp->curr_sect + 1; /* Get current sector */ + } else { /* On the cluster boundary, get next cluster */ + if (fp->fptr == 0) { /* Is top of the file */ + clust = fp->org_clust; + if (clust == 0) /* No cluster is created yet */ + fp->org_clust = clust = create_chain(fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of file */ + clust = create_chain(fs, fp->curr_clust); /* Trace or streach cluster chain */ + } + if (clust == 0) break; /* Disk full */ + if (clust == 1 || clust >= fs->max_clust) goto fw_error; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(fs, clust); /* Get current sector */ + fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */ + } + if (fp->flag & FA__DIRTY) { /* Flush file I/O buffer if needed */ + if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fw_error; + fp->flag &= ~FA__DIRTY; + } + fp->curr_sect = sect; /* Update current sector */ + cc = btw / S_SIZ; /* When left bytes >= S_SIZ, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (cc > fp->sect_clust) cc = fp->sect_clust; + if (diskWrite(fs->drive, wbuff, sect, cc) != RES_OK) + goto fw_error; + fp->sect_clust -= cc - 1; + fp->curr_sect += cc - 1; + wcnt = cc * S_SIZ; continue; + } + if (fp->fptr < fp->fsize && /* Fill sector buffer with file data if needed */ + diskRead(fs->drive, fp->buffer, sect, 1) != RES_OK) + goto fw_error; + } + wcnt = S_SIZ - ((U16)fp->fptr & (S_SIZ - 1)); /* Copy fractional bytes to file I/O buffer */ + if (wcnt > btw) wcnt = btw; + memcpy(&fp->buffer[fp->fptr & (S_SIZ - 1)], wbuff, wcnt); + fp->flag |= FA__DIRTY; + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + return FR_OK; + +fw_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize between File and Disk */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ + ) +{ + U32 tim; + U8 *dir; + FRESULT res; + FATFS *fs = fp->fs; + + + res = validate(fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ + /* Write back data buffer if needed */ + if (fp->flag & FA__DIRTY) { + if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + return FR_RW_ERROR; + fp->flag &= ~FA__DIRTY; + } + /* Update the directory entry */ + if (!move_window(fs, fp->dir_sect)) + return FR_RW_ERROR; + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_U32(&dir[DIR_FileSize], fp->fsize); /* Update file size */ + ST_U16(&dir[DIR_FstClusLO], fp->org_clust); /* Update start cluster */ + ST_U16(&dir[DIR_FstClusHI], fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_U32(&dir[DIR_WrtTime], tim); + fp->flag &= ~FA__WRITTEN; + res = sync(fs); + } + } + return res; +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ + ) +{ + FRESULT res; + + +#if !_FS_READONLY + res = f_sync(fp); +#else + res = validate(fp->fs, fp->id); +#endif + if (res == FR_OK) + fp->fs = NULL; + return res; +} + + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + U32 ofs /* File pointer from top of file */ + ) +{ + U32 clust, csize; + U8 csect; + FRESULT res; + FATFS *fs = fp->fs; + + + res = validate(fs, fp->id); /* Check validity of the object */ + if (res) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (diskWrite(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fk_error; + fp->flag &= ~FA__DIRTY; + } + if (ofs > fp->fsize && !(fp->flag & FA_WRITE)) +#else + if (ofs > fp->fsize) +#endif + ofs = fp->fsize; + fp->fptr = 0; fp->sect_clust = 1; /* Set file R/W pointer to top of the file */ + + /* Move file R/W pointer if needed */ + if (ofs) { + clust = fp->org_clust; /* Get start cluster */ +#if !_FS_READONLY + if (!clust) { /* If the file does not have a cluster chain, create new cluster chain */ + clust = create_chain(fs, 0); + if (clust == 1) goto fk_error; + fp->org_clust = clust; + } +#endif + if (clust) { /* If the file has a cluster chain, it can be followed */ + csize = (U32)fs->sects_clust * S_SIZ; /* Cluster size in unit of byte */ + for (;;) { /* Loop to skip leading clusters */ + fp->curr_clust = clust; /* Update current cluster */ + if (ofs <= csize) break; +#if !_FS_READONLY + if (fp->flag & FA_WRITE) /* Check if in write mode or not */ + clust = create_chain(fs, clust); /* Force streached if in write mode */ + else +#endif + clust = get_cluster(fs, clust); /* Only follow cluster chain if not in write mode */ + if (clust == 0) { /* Stop if could not follow the cluster chain */ + ofs = csize; break; + } + if (clust == 1 || clust >= fs->max_clust) goto fk_error; + fp->fptr += csize; /* Update R/W pointer */ + ofs -= csize; + } + csect = (U8)((ofs - 1) / S_SIZ); /* Sector offset in the cluster */ + fp->curr_sect = clust2sect(fs, clust) + csect; /* Current sector */ + if ((ofs & (S_SIZ - 1)) && /* Load current sector if needed */ + diskRead(fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fk_error; + fp->sect_clust = fs->sects_clust - csect; /* Left sector counter in the cluster */ + fp->fptr += ofs; /* Update file R/W pointer */ + } + } +#if !_FS_READONLY + if ((fp->flag & FA_WRITE) && fp->fptr > fp->fsize) { /* Set updated flag if in write mode */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + return FR_OK; + +fk_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a directroy object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR *dirobj, /* Pointer to directory object to create */ + const char *path /* Pointer to the directory path */ + ) +{ + U8 *dir; + char fn[8+3+1]; + FRESULT res; + FATFS *fs; + + + res = auto_mount(&path, &fs, 0); + if (res != FR_OK) return res; + dirobj->fs = fs; + + res = trace_path(dirobj, fn, path, &dir); /* Trace the directory path */ + if (res == FR_OK) { /* Trace completed */ + if (dir != NULL) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */ + dirobj->clust = ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]); + dirobj->sect = clust2sect(fs, dirobj->clust); + dirobj->index = 2; + } else { /* The entry is not a directory */ + res = FR_NO_FILE; + } + } + dirobj->id = fs->id; + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR *dirobj, /* Pointer to the directory object */ + FILINFO *finfo /* Pointer to file information to return */ + ) +{ + U8 *dir, c, res; + FATFS *fs = dirobj->fs; + + + res = validate(fs, dirobj->id); /* Check validity of the object */ + if (res) return (FRESULT) res; + + finfo->fname[0] = 0; + while (dirobj->sect) { + if (!move_window(fs, dirobj->sect)) + return FR_RW_ERROR; + dir = &fs->win[(dirobj->index & ((S_SIZ - 1) >> 5)) * 32]; /* pointer to the directory entry */ + c = *dir; + if (c == 0) break; /* Has it reached to end of dir? */ + if (c != 0xE5 && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + get_fileinfo(finfo, dir); + if (!next_dir_entry(dirobj)) dirobj->sect = 0; /* Next entry */ + if (finfo->fname[0]) break; /* Found valid entry */ + } + + return FR_OK; +} + + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const char *path, /* Pointer to the file path */ + FILINFO *finfo /* Pointer to file information to return */ + ) +{ + U8 *dir; + char fn[8+3+1]; + FRESULT res; + DIR dirobj; + FATFS *fs; + + + res = auto_mount(&path, &fs, 0); + if (res != FR_OK) return res; + dirobj.fs = fs; + + res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) { /* Trace completed */ + if (dir) /* Found an object */ + get_fileinfo(finfo, dir); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + + return res; +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const char *drv, /* Logical drive number */ + U32 *nclust, /* Pointer to the double word to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to the file system object to return */ + ) +{ + U32 n, clust, sect; + U8 fat, f, *p; + FRESULT res; + FATFS *fs; + + + /* Get drive number */ + res = auto_mount(&drv, &fs, 0); + if (res != FR_OK) return res; + *fatfs = fs; + + /* If number of free cluster is valid, return it without cluster scan. */ + if (fs->free_clust <= fs->max_clust - 2) { + *nclust = fs->free_clust; + return FR_OK; + } + + /* Count number of free clusters */ + fat = fs->fs_type; + n = 0; + if (fat == FS_FAT12) { + clust = 2; + do { + if ((U16)get_cluster(fs, clust) == 0) n++; + } while (++clust < fs->max_clust); + } else { + clust = fs->max_clust; + sect = fs->fatbase; + f = 0; p = 0; + do { + if (!f) { + if (!move_window(fs, sect++)) return FR_RW_ERROR; + p = fs->win; + } + if (fat == FS_FAT16) { + if (LD_U16(p) == 0) n++; + p += 2; f += 1; + } else { + if (LD_U32(p) == 0) n++; + p += 4; f += 2; + } + } while (--clust); + } + fs->free_clust = n; +#if _USE_FSINFO + if (fat == FS_FAT32) fs->fsi_flag = 1; +#endif + + *nclust = n; + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const char *path /* Pointer to the file or directory path */ + ) +{ + U8 *dir, *sdir; + U32 dclust, dsect; + char fn[8+3+1]; + FRESULT res; + DIR dirobj; + FATFS *fs; + + + res = auto_mount(&path, &fs, 1); + if (res != FR_OK) return res; + dirobj.fs = fs; + + res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */ + if (res != FR_OK) return res; /* Trace failed */ + if (dir == NULL) return FR_INVALID_NAME; /* It is the root directory */ + if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */ + dsect = fs->winsect; + dclust = ((U32)LD_U16(&dir[DIR_FstClusHI]) << 16) | LD_U16(&dir[DIR_FstClusLO]); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ + dirobj.clust = dclust; /* Check if the sub-dir is empty or not */ + dirobj.sect = clust2sect(fs, dclust); + dirobj.index = 2; + do { + if (!move_window(fs, dirobj.sect)) return FR_RW_ERROR; + sdir = &fs->win[(dirobj.index & ((S_SIZ - 1) >> 5)) * 32]; + if (sdir[DIR_Name] == 0) break; + if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL)) + return FR_DENIED; /* The directory is not empty */ + } while (next_dir_entry(&dirobj)); + } + + if (!move_window(fs, dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */ + dir[DIR_Name] = 0xE5; + fs->winflag = 1; + if (!remove_chain(fs, dclust)) return FR_RW_ERROR; /* Remove the cluster chain */ + + return sync(fs); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const char *path /* Pointer to the directory path */ + ) +{ + U8 *dir, *fw, n; + char fn[8+3+1]; + U32 sect, dsect, dclust, pclust, tim; + FRESULT res; + DIR dirobj; + FATFS *fs; + + res = auto_mount(&path, &fs, 1); + if (res != FR_OK) return res; + dirobj.fs = fs; + + res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) return res; + + res = reserve_direntry(&dirobj, &dir); /* Reserve a directory entry */ + if (res != FR_OK) return res; + sect = fs->winsect; + + dclust = create_chain(fs, 0); /* Allocate a cluster for new directory table */ + if (dclust == 1) return FR_RW_ERROR; + + dsect = clust2sect(fs, dclust); + if (!dsect) return FR_DENIED; + + if (!move_window(fs, dsect)) return FR_RW_ERROR; + + fw = fs->win; + memset(fw, 0, S_SIZ); /* Clear the new directory table */ + for (n = 1; n < fs->sects_clust; n++) { + if (diskWrite(fs->drive, fw, ++dsect, 1) != RES_OK) + return FR_RW_ERROR; + } + memset(&fw[DIR_Name], ' ', 8+3); /* Create "." entry */ + fw[DIR_Name] = '.'; + fw[DIR_Attr] = AM_DIR; + tim = get_fattime(); + ST_U32(&fw[DIR_WrtTime], tim); + memcpy(&fw[32], &fw[0], 32); fw[33] = '.'; /* Create ".." entry */ + pclust = dirobj.sclust; +#if _FAT32 + ST_U16(&fw[ DIR_FstClusHI], dclust >> 16); + if (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) pclust = 0; + ST_U16(&fw[32+DIR_FstClusHI], pclust >> 16); +#endif + ST_U16(&fw[ DIR_FstClusLO], dclust); + ST_U16(&fw[32+DIR_FstClusLO], pclust); + fs->winflag = 1; + + if (!move_window(fs, sect)) return FR_RW_ERROR; + memset(&dir[0], 0, 32); /* Initialize the new entry */ + memcpy(&dir[DIR_Name], fn, 8+3); /* Name */ + dir[DIR_NTres] = fn[11]; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_U32(&dir[DIR_WrtTime], tim); /* Crated time */ + ST_U16(&dir[DIR_FstClusLO], dclust); /* Table start cluster */ + ST_U16(&dir[DIR_FstClusHI], dclust >> 16); + + return sync(fs); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const char *path, /* Pointer to the file path */ + U8 value, /* Attribute bits */ + U8 mask /* Attribute mask to change */ + ) +{ + FRESULT res; + U8 *dir; + DIR dirobj; + char fn[8+3+1]; + FATFS *fs; + + + res = auto_mount(&path, &fs, 1); + if (res == FR_OK) { + dirobj.fs = fs; + res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) { /* Trace completed */ + if (dir == NULL) { + res = FR_INVALID_NAME; + } else { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (U8)~mask); /* Apply attribute change */ + res = sync(fs); + } + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const char *path_old, /* Pointer to the old name */ + const char *path_new /* Pointer to the new name */ + ) +{ + FRESULT res; + U32 sect_old; + U8 *dir_old, *dir_new, direntry[32-11]; + DIR dirobj; + char fn[8+3+1]; + FATFS *fs; + + + res = auto_mount(&path_old, &fs, 1); + if (res != FR_OK) return res; + dirobj.fs = fs; + + res = trace_path(&dirobj, fn, path_old, &dir_old); /* Check old object */ + if (res != FR_OK) return res; /* The old object is not found */ + if (!dir_old) return FR_NO_FILE; + sect_old = fs->winsect; /* Save the object information */ + memcpy(direntry, &dir_old[DIR_Attr], 32-11); + + res = trace_path(&dirobj, fn, path_new, &dir_new); /* Check new object */ + if (res == FR_OK) return FR_EXIST; /* The new object name is already existing */ + if (res != FR_NO_FILE) return res; /* Is there no old name? */ + res = reserve_direntry(&dirobj, &dir_new); /* Reserve a directory entry */ + if (res != FR_OK) return res; + + memcpy(&dir_new[DIR_Attr], direntry, 32-11); /* Create new entry */ + memcpy(&dir_new[DIR_Name], fn, 8+3); + dir_new[DIR_NTres] = fn[11]; + fs->winflag = 1; + + if (!move_window(fs, sect_old)) return FR_RW_ERROR; /* Remove old entry */ + dir_old[DIR_Name] = 0xE5; + + return sync(fs); +} + + + +#if _USE_MKFS +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ + +#define N_ROOTDIR 512 +#define N_FATS 1 +#define MAX_SECTOR 64000000UL +#define MIN_SECTOR 2000UL +#define ERASE_BLK 32 + + +FRESULT f_mkfs ( + U8 drv, /* Logical drive number */ + U8 partition, /* Partitioning rule 0:FDISK, 1:SFD */ + U8 allocsize /* Allocation unit size [sectors] */ + ) +{ + U8 fmt, m, *tbl; + U32 b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ + U32 n_part, n_rsv, n_fat, n_dir; /* Area size */ + U32 n_clust, n; + FATFS *fs; + DSTATUS stat; + + + /* Check and mounted drive and clear work area */ + if (drv >= _DRIVES) + return FR_INVALID_DRIVE; + + if (!(fs = FatFs [drv])) + return FR_NOT_ENABLED; + + memset (fs, 0, sizeof(FATFS)); + drv = LD2PD(drv); + + /* Check validity of the parameters */ + for (n = 1; n <= 64 && allocsize != n; n <<= 1) + ; + if (n > 64 || partition >= 2) + { // ### + // printf ("line %d: n > 64 || partition >= 2. n=%d, partition=%d\n", __LINE__, n, partition); // ### + return FR_MKFS_ABORTED; + } // ### + + /* Get disk statics */ + stat = diskInitialize (drv); + + if (stat & STA_NOINIT) + return FR_NOT_READY; + if (stat & STA_PROTECT) + return FR_WRITE_PROTECTED; + { // ### + DRESULT dres; // ### + if ((dres = diskIoctl (drv, GET_SECTOR_COUNT, &n_part)) != RES_OK || n_part < MIN_SECTOR) + { // ### + // printf ("line %d: ioctl returned %d. n_part=%d, MIN_SECTOR=%d\n", __LINE__, dres, n_part, MIN_SECTOR); // ### + return FR_MKFS_ABORTED; + } // ### + } // ### + + if (n_part > MAX_SECTOR) + n_part = MAX_SECTOR; + + b_part = (!partition) ? 63 : 0; + n_part -= b_part; +#if S_MAX_SIZ > 512 /* Check disk sector size */ + if (diskIoctl(drv, GET_SECTOR_SIZE, &S_SIZ) != RES_OK + || S_SIZ > S_MAX_SIZ + || (U32)S_SIZ * allocsize > 32768U) + return FR_MKFS_ABORTED; +#endif + + /* Pre-compute number of clusters and FAT type */ + n_clust = n_part / allocsize; + fmt = FS_FAT12; + if (n_clust >= 0xFF7) fmt = FS_FAT16; + if (n_clust >= 0xFFF7) fmt = FS_FAT32; + switch (fmt) { + case FS_FAT12: + n_fat = ((n_clust * 3 + 1) / 2 + 3 + S_SIZ - 1) / S_SIZ; + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / S_SIZ; + break; + case FS_FAT16: + n_fat = ((n_clust * 2) + 4 + S_SIZ - 1) / S_SIZ; + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / S_SIZ; + break; + default: + n_fat = ((n_clust * 4) + 8 + S_SIZ - 1) / S_SIZ; + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + +#ifdef ERASE_BLK + /* Round up data start sector to erase block boundary */ + n = (b_data + ERASE_BLK - 1) & ~(ERASE_BLK - 1); + b_dir += n - b_data; + n_fat += (n - b_data) / N_FATS; +#endif + /* Determine number of cluster and final check of validity of the FAT type */ + n_clust = (n_part - n_rsv - n_fat * 2 - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clust < 0xFF7) + || (fmt == FS_FAT32 && n_clust < 0xFFF7)) + { // ### + // printf ("line %d: fmt=%d, FS_FAT16=%d, FS_FAT32=%d, n_clust=0x%x\n", __LINE__, fmt, FS_FAT16, FS_FAT32, n_clust); // ### + return FR_MKFS_ABORTED; + } // ### + + /* Create partition table if needed */ + if (!partition) { + U32 n_disk = b_part + n_part; + + tbl = &fs->win[MBR_Table]; + ST_U32(&tbl[0], 0x00010180); /* Partition start in CHS */ + if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ + n_disk = n_disk / 63 / 255; + tbl[7] = (U8)n_disk; + tbl[6] = (U8)((n_disk >> 2) | 63); + } else { + ST_U16(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_U32(&tbl[8], 63); /* Partition start in LBA */ + ST_U32(&tbl[12], n_part); /* Partition size in LBA */ + ST_U16(&tbl[64], 0xAA55); /* Signature */ + if (diskWrite(drv, fs->win, 0, 1) != RES_OK) + return FR_RW_ERROR; + } + + /* Create boot record */ + memset(tbl = fs->win, 0, S_SIZ); + ST_U32(&tbl[BS_jmpBoot], 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_U16(&tbl[BPB_BytsPerSec], S_SIZ); /* Sector size */ + tbl[BPB_SecPerClus] = (U8)allocsize; /* Sectors per cluster */ + ST_U16(&tbl[BPB_RsvdSecCnt], n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + ST_U16(&tbl[BPB_RootEntCnt], S_SIZ / 32 * n_dir); /* Number of rootdir entries */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_U16(&tbl[BPB_TotSec16], n_part); + } else { + ST_U32(&tbl[BPB_TotSec32], n_part); + } + tbl[BPB_Media] = 0xF8; /* Media descripter */ + ST_U16(&tbl[BPB_SecPerTrk], 63); /* Number of sectors per track */ + ST_U16(&tbl[BPB_NumHeads], 255); /* Number of heads */ + ST_U32(&tbl[BPB_HiddSec], b_part); /* Hidden sectors */ + if (fmt != FS_FAT32) { + ST_U16(&tbl[BPB_FATSz16], n_fat); /* Number of secters per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + memcpy(&tbl[BS_VolLab], "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ + } else { + ST_U32(&tbl[BPB_FATSz32], n_fat); /* Number of secters per FAT */ + ST_U32(&tbl[BPB_RootClus], 2); /* Root directory cluster (2) */ + ST_U16(&tbl[BPB_FSInfo], 1); /* FSInfo record (bs+1) */ + ST_U16(&tbl[BPB_BkBootSec], 6); /* Backup boot record (bs+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + memcpy(&tbl[BS_VolLab32], "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + } + ST_U16(&tbl[BS_55AA], 0xAA55); /* Signature */ + if (diskWrite(drv, tbl, b_part+0, 1) != RES_OK) + return FR_RW_ERROR; + if (fmt == FS_FAT32) + diskWrite(drv, tbl, b_part+6, 1); + + /* Initialize FAT area */ + for (m = 0; m < N_FATS; m++) { + memset(tbl, 0, S_SIZ); /* 1st sector of the FAT */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8; + ST_U32(&tbl[0], n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + ST_U32(&tbl[0], 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ + ST_U32(&tbl[4], 0xFFFFFFFF); + ST_U32(&tbl[8], 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (diskWrite(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + memset(tbl, 0, S_SIZ); /* Following FAT entries are filled by zero */ + for (n = 1; n < n_fat; n++) { + if (diskWrite(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + } + } + + /* Initialize Root directory */ + for (m = 0; m < 64; m++) { + if (diskWrite(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + } + + /* Create FSInfo record if needed */ + if (fmt == FS_FAT32) { + ST_U16(&tbl[BS_55AA], 0xAA55); + ST_U32(&tbl[FSI_LeadSig], 0x41615252); + ST_U32(&tbl[FSI_StrucSig], 0x61417272); + ST_U32(&tbl[FSI_Free_Count], n_clust - 1); + ST_U32(&tbl[FSI_Nxt_Free], 0xFFFFFFFF); + diskWrite(drv, tbl, b_part+1, 1); + diskWrite(drv, tbl, b_part+7, 1); + } + + return (diskIoctl(drv, CTRL_SYNC, NULL) == RES_OK) ? FR_OK : FR_RW_ERROR; +} + +#endif /* _USE_MKFS */ +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + diff --git a/fatfs/ff.h b/fatfs/ff.h new file mode 100644 index 0000000..70d64d9 --- /dev/null +++ b/fatfs/ff.h @@ -0,0 +1,338 @@ +/*--------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.04b (C)ChaN, 2007 +/---------------------------------------------------------------------------/ +/ FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is a free software and is opened for education, +/ research and development under license policy of following trems. +/ +/ Copyright (C) 2007, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is no warranty. +/ * You can use, modify and/or redistribute it for personal, non-profit or +/ profit use without any restriction under your responsibility. +/ * Redistributions of source code must retain the above copyright notice. +/ +/---------------------------------------------------------------------------*/ + +#ifndef _FATFS_H_ +#define _FATFS_H_ + +#define _MCU_ENDIAN 2 +/* The _MCU_ENDIAN defines which access method is used to the FAT structure. +/ 1: Enable word access. +/ 2: Disable word access and use byte-by-byte access instead. +/ When the architectural byte order of the MCU is big-endian and/or address +/ miss-aligned access is prohibited, the _MCU_ENDIAN must be set to 2. +/ If it is not the case, it can be set to 1 for good code efficiency. */ + +#define _FS_READONLY 0 +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename +/ and useless f_getfree. */ + +#define _FS_MINIMIZE 0 +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod and f_rename are removed. +/ 2: f_opendir and f_readdir are removed in addition to level 1. +/ 3: f_lseek is removed in addition to level 2. */ + +#define _DRIVES 1 +/* Number of logical drives to be used. This affects the size of internal table. */ + +#define _USE_MKFS 1 +/* When _USE_MKFS is set to 1 and _FS_READONLY is set to 0, f_mkfs function is +/ enabled. */ + +#define _MULTI_PARTITION 0 +/* When _MULTI_PARTITION is set to 0, each logical drive is bound to same +/ physical drive number and can mount only 1st primaly partition. When it is +/ set to 1, each logical drive can mount a partition listed in Drives[]. */ + +#define _USE_FSINFO 1 +/* To enable FSInfo support on FAT32 volume, set _USE_FSINFO to 1. */ + +#define _USE_SJIS 1 +/* When _USE_SJIS is set to 1, Shift-JIS code transparency is enabled, otherwise +/ only US-ASCII(7bit) code can be accepted as file/directory name. */ + +#define _USE_NTFLAG 1 +/* When _USE_NTFLAG is set to 1, upper/lower case of the file name is preserved. +/ Note that the files are always accessed in case insensitive. */ + +#include "sysdefs.h" + +// +// Definitions corresponds to multiple sector size (not tested) +// +#define S_MAX_SIZ 512 /* Do not change */ +#if S_MAX_SIZ > 512 +#define S_SIZ (fs->s_size) +#else +#define S_SIZ 512 +#endif + +// +// File system object structure +// +typedef struct _FATFS +{ + U16 id; /* File system mount ID */ + U16 n_rootdir; /* Number of root directory entries */ + U32 winsect; /* Current sector appearing in the win[] */ + U32 sects_fat; /* Sectors per fat */ + U32 max_clust; /* Maximum cluster# + 1 */ + U32 fatbase; /* FAT start sector */ + U32 dirbase; /* Root directory start sector (cluster# for FAT32) */ + U32 database; /* Data start sector */ +#if _FS_READONLY == 0 + U32 last_clust; /* Last allocated cluster */ + U32 free_clust; /* Number of free clusters */ +#if _USE_FSINFO + U32 fsi_sector; /* fsinfo sector */ + U8 fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + U8 pad2; +#endif +#endif + U8 fs_type; /* FAT sub type */ + U8 sects_clust; /* Sectors per cluster */ +#if S_MAX_SIZ > 512 + U16 s_size; /* Sector size */ +#endif + U8 n_fats; /* Number of FAT copies */ + U8 drive; /* Physical drive number */ + U8 winflag; /* win[] dirty flag (1:must be written back) */ + U8 pad1; + U8 win[S_MAX_SIZ]; /* Disk access window for Directory/FAT */ +} +/* __attribute__ ((packed)) */ FATFS; + +// +// Directory object structure +// +typedef struct _DIR +{ + U16 id; /* Owner file system mount ID */ + U16 index; /* Current index */ + FATFS* fs; /* Pointer to the owner file system object */ + U32 sclust; /* Start cluster */ + U32 clust; /* Current cluster */ + U32 sect; /* Current sector */ +} +/* __attribute__ ((packed)) */ DIR; + +// +// File object structure +// +typedef struct _FIL +{ + U16 id; /* Owner file system mount ID */ + U8 flag; /* File status flags */ + U8 sect_clust; /* Left sectors in cluster */ + FATFS* fs; /* Pointer to the owner file system object */ + U32 fptr; /* File R/W pointer */ + U32 fsize; /* File size */ + U32 org_clust; /* File start cluster */ + U32 curr_clust; /* Current cluster */ + U32 curr_sect; /* Current sector */ +#if _FS_READONLY == 0 + U32 dir_sect; /* Sector containing the directory entry */ + U8* dir_ptr; /* Ponter to the directory entry in the window */ +#endif + U8 buffer [S_MAX_SIZ]; /* File R/W buffer */ +} +/* __attribute__ ((packed)) */ FIL; + +// +// File status structure +// +typedef struct _FILINFO +{ + U32 fsize; /* Size */ + U16 fdate; /* Date */ + U16 ftime; /* Time */ + U8 fattrib; /* Attribute */ + char fname [8+1+3+1]; /* Name (8.3 format) */ +} +/* __attribute__ ((packed)) */ FILINFO; + +// +// Definitions corresponds to multi partition +// +#if _MULTI_PARTITION != 0 /* Multiple partition cfg */ + +typedef struct _PARTITION +{ + U8 pd; /* Physical drive # (0-255) */ + U8 pt; /* Partition # (0-3) */ +} +/* __attribute__ ((packed)) */ PARTITION; + +extern const PARTITION Drives []; /* Logical drive# to physical location conversion table */ +#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ +#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ + +#else /* Single partition cfg */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ + +#endif + +// +// File function return code (FRESULT) +// +typedef enum +{ + FR_OK = 0, /* 0 */ + FR_NOT_READY, /* 1 */ + FR_NO_FILE, /* 2 */ + FR_NO_PATH, /* 3 */ + FR_INVALID_NAME, /* 4 */ + FR_INVALID_DRIVE, /* 5 */ + FR_DENIED, /* 6 */ + FR_EXIST, /* 7 */ + FR_RW_ERROR, /* 8 */ + FR_WRITE_PROTECTED, /* 9 */ + FR_NOT_ENABLED, /* 10 */ + FR_NO_FILESYSTEM, /* 11 */ + FR_INVALID_OBJECT, /* 12 */ + FR_MKFS_ABORTED /* 13 */ +} +FRESULT; + +// +// FatFs module application interface +// +void f_printerror (FRESULT f); /* Print error code */ +FRESULT f_mount (U8, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const char*, U8); /* Open or create a file */ +FRESULT f_read (FIL*, void*, U16, U16*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, U16, U16*); /* Write data to a file */ +FRESULT f_lseek (FIL*, U32); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (DIR*, const char*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const char*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const char*, U32*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const char*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const char*); /* Create a new directory */ +FRESULT f_chmod (const char*, U8, U8); /* Change file/dir attriburte */ +FRESULT f_rename (const char*, const char*); /* Rename/Move a file or directory */ +FRESULT f_mkfs (U8, U8, U8); /* Create a file system on the drive */ + +// +// User defined function to give a current time to fatfs module +// +U32 get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ + /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ + +// +// File access control and file status flags (FIL.flag) +// +#define FA_OPEN_EXISTING 0x00 +#define FA_READ 0x01 +#if _FS_READONLY == 0 +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + +// +// FAT sub type (FATFS.fs_type) +// +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + +// +// File attribute bits for directory entry +// +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + +// +// Offset of FAT structure members +// +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 + +// +// Multi-byte word access macros +// +#if _MCU_ENDIAN == 1 /* Use word access */ +#define LD_U16(ptr) (U16)(*(U16*)(U8*)(ptr)) +#define LD_U32(ptr) (U32)(*(U32*)(U8*)(ptr)) +#define ST_U16(ptr,val) *(U16*)(U8*)(ptr)=(U16)(val) +#define ST_U32(ptr,val) *(U32*)(U8*)(ptr)=(U32)(val) +#else +#if _MCU_ENDIAN == 2 /* Use byte-by-byte access */ +#define LD_U16(ptr) (U16)(((U16)*(U8*)((ptr)+1)<<8)|(U16)*(U8*)(ptr)) +#define LD_U32(ptr) (U32)(((U32)*(U8*)((ptr)+3)<<24)|((U32)*(U8*)((ptr)+2)<<16)|((U16)*(U8*)((ptr)+1)<<8)|*(U8*)(ptr)) +#define ST_U16(ptr,val) *(U8*)(ptr)=(U8)(val); *(U8*)((ptr)+1)=(U8)((U16)(val)>>8) +#define ST_U32(ptr,val) *(U8*)(ptr)=(U8)(val); *(U8*)((ptr)+1)=(U8)((U16)(val)>>8); *(U8*)((ptr)+2)=(U8)((U32)(val)>>16); *(U8*)((ptr)+3)=(U8)((U32)(val)>>24) +#else +#error Do not forget to set _MCU_ENDIAN properly! +#endif +#endif + +#endif diff --git a/fatfs/mmc.c b/fatfs/mmc.c new file mode 100644 index 0000000..70b5f36 --- /dev/null +++ b/fatfs/mmc.c @@ -0,0 +1,944 @@ +/************************************************************************* + * + * Used with ICCARM and AARM. + * + * (c) Copyright IAR Systems 2005 + * + * File name : mmc.c + * Description : MMC + * + * History : + * 1. Data : July 1, 2005 + * Author : Stanimir Bonev + * Description : Create + * + * $Revision: 1.4 $ + * + * (C) Joel Winarske, 2006,2007 +**************************************************************************/ +#include // ### +#include "disk.h" +#include "diskio.h" +#include "mmc.h" +#include "spi.h" + +#define MMC_RET_ERROR(Res) do { mmcLastError = Res; return (MmcCardError); } while (0) +#define MMC_RET_DATA_ERR(Res) do { mmcLastError = Res; return (MmcDataError); } while (0) + +#define CSD_GET_TRAN_SPEED_EXP() (mmcSdCsd [ 3] & 0x07) +#define CSD_GET_TRAN_SPEED_MANT() ((mmcSdCsd [ 3] & 0xF8) >> 3) +#define CSD_GET_NSAC() (mmcSdCsd [ 2]) +#define CSD_GET_TAAC_EXP() (mmcSdCsd [ 1] & 0x7) +#define CSD_GET_TAAC_MANT() ((mmcSdCsd [ 1] & 0xF8) >> 3) +#define CSD_GET_R2W_FACTOR() ((mmcSdCsd [12] & 0x1C) >> 2) +#define CSD_GET_READ_BL_LEN() (mmcSdCsd [ 5] & 0x0F) +#define CSD_GET_C_SIZE() (((mmcSdCsd [ 6] & 0x03) << 10) + (mmcSdCsd [7] << 2) + ((mmcSdCsd [8] & 0xc0) >> 6)) +#define CSD_GET_C_SIZE_MULT() (((mmcSdCsd [ 9] & 0x03) << 1) + ((mmcSdCsd [10] & 0x80) >> 7)) +#define CSD_GET_PERM_WRITE_PROTECT() ((mmcSdCsd [14] & 0x20) >> 5) +#define CSD_GET_TMP_WRITE_PROTECT() ((mmcSdCsd [14] & 0x10) >> 4) + +static const U32 mmcTransfExp [] = +{ + 10000UL, + 100000UL, + 1000000UL, + 10000000UL, + 0UL, + 0UL, + 0UL, + 0UL, +}; + +static const U32 mmmcAccessTime [] = +{ + 10000000UL, + 1000000UL, + 100000UL, + 10000UL, + 1000UL, + 100UL, + 10UL, + 1UL, +}; + +static const U32 mmcCsdMant [] = +{ + 0UL,10UL,12UL,13UL,15UL, + 20UL,25UL, + 30UL,35UL, + 40UL,45UL, + 50UL,55UL, + 60UL, + 70UL, + 80UL, +}; + +static const U32 mmcAccessTimeMant [] = +{ + 0UL,100UL,83UL,77UL,67UL, + 50UL,40UL, + 33UL,29UL, + 25UL,22UL, + 20UL,18UL, + 17UL, + 14UL, + 13UL, +}; + +static const mmcCommads_t mmcCmd [CMD_END] = +{ + { 0x40, MmcNoArg, MmcR1 }, // CMD0 + { 0x41, MmcNoArg, MmcR1 }, // CMD1 + { 0x49, MmcNoArg, MmcR1 }, // CMD9 + { 0x4A, MmcNoArg, MmcR1 }, // CMD10 + { 0x4C, MmcNoArg, MmcR1 }, // CMD12 + { 0x4D, MmcNoArg, MmcR2 }, // CMD13 + { 0x50, MmcBlockLen, MmcR1 }, // CMD16 + { 0x51, MmcDataAdd, MmcR1 }, // CMD17 + { 0x52, MmcDataAdd, MmcR1 }, // CMD18 + { 0x58, MmcDataAdd, MmcR1 }, // CMD24 + { 0x59, MmcDataAdd, MmcR1 }, // CMD25 + { 0x5B, MmcNoArg, MmcR1 }, // CMD27 + { 0x5C, MmcDataAdd, MmcR1b}, // CMD28 + { 0x5D, MmcDataAdd, MmcR1b}, // CMD29 + { 0x5E, MmcDataAdd, MmcR1 }, // CMD30 + { 0x60, MmcDataAdd, MmcR1 }, // CMD32 + { 0x61, MmcDataAdd, MmcR1 }, // CMD33 + { 0x62, MmcDataAdd, MmcR1 }, // CMD34 + { 0x63, MmcDataAdd, MmcR1 }, // CMD35 + { 0x64, MmcDataAdd, MmcR1 }, // CMD36 + { 0x65, MmcDataAdd, MmcR1 }, // CMD37 + { 0x66, MmcDummyWord, MmcR1b}, // CMD38 + { 0x6A, MmcDummyWord, MmcR1b}, // CMD42 + { 0x77, MmcNoArg, MmcR1 }, // CMD55 + { 0x78, MmcNoArg, MmcR1 }, // CMD56 + { 0x7A, MmcNoArg, MmcR3 }, // CMD58 + { 0x7B, MmcDummyWord, MmcR1 }, // CMD59 + { 0x69, MmcNoArg, MmcR1 } // ACMD41 +}; + +static volatile DSTATUS Stat = STA_NOINIT; /* Disk status */ +static DiskStatus_t mmcDskStatus; +static U32 mmcLastError; +static U32 Tnac; +static U32 Twr; +static U8 mmcSdCsd [16]; + +// +// +// +static U32 mmcSendCmd (mmcSpiCmdInd_t ComdInd, U32 Arg); +static U32 mmcSetBlockLen (U32 length); +static void mmcCSDImplement (void); +static mmcState_t mmcInitMedia (void); +static mmcState_t mmcReadCardInfo (U8 *pData, mmcSpiCmdInd_t Command); +static inline mmcState_t mmcRead (U8 *pData, U32 Add, U32 Length); +static inline mmcState_t mmcWrite (U8 *pData, U32 Add, U32 Length); +static inline mmcState_t mmcVerify (U8 *pData, U32 Add, U32 Length); + +/************************************************************************* + * Function Name: mmcSendCmd + * Parameters: MmcSpiCmdInd_t ComdInd,U32 Arg + * + * Return: U32 + * + * Description: MMC commands implemet + * + *************************************************************************/ +static U32 mmcSendCmd (mmcSpiCmdInd_t ComdInd, U32 Arg) +{ + U32 ch = 0xff; + U32 i; + + // + // Chip Select + // + spiChipSelect (1); + + // + // Send command code + // + spiTransferByte (mmcCmd [ComdInd].TxData); + + // + // Send command's arguments + // + if (mmcCmd [ComdInd].Arg == MmcNoArg) + { + spiTransferByte (0x00); + spiTransferByte (0x00); + spiTransferByte (0x00); + spiTransferByte (0x00); + } + else + { + spiTransferByte (Arg >> 24); + spiTransferByte (Arg >> 16); + spiTransferByte (Arg >> 8); + spiTransferByte (Arg); + } + + // + // Send CRC + // + if (ComdInd == CMD0) + spiTransferByte (0x95); + else + spiTransferByte (0xff); + + for (i = 9; i && (ch == 0xff); --i) + ch = spiTransferByte (0xff); + + if (i == 0) + { + spiChipSelect (0); + return ((U32) -1); + } + + switch (mmcCmd [ComdInd].Resp) + { + case MmcR1b : + { + U32 busy; + + for (busy = 0, i = Twr; i && (busy != 0xff); --i) + busy = spiTransferByte (0xff); + } + return (ch); + + case MmcR1 : + return (ch); + + case MmcR2 : + Arg = ((U32) ch << 8) & 0x0000FF00; + Arg |= spiTransferByte (0xff) & 0xff; + return (Arg); + + case MmcR3 : + default: + Arg = ((U32) ch << 24) & 0xff000000; + Arg |= ((U32) spiTransferByte (0xff) << 16) & 0x00FF0000; + Arg |= ((U32) spiTransferByte (0xff) << 8 ) & 0x0000FF00; + Arg |= spiTransferByte (0xff) & 0xff; + return (Arg); + } +} + +/************************************************************************* + * Function Name: mmcSetBlockLen + * Parameters: U32 Length + * + * Return: U32 + * + * Description: Set Block length Return command's result + * + *************************************************************************/ +static U32 mmcSetBlockLen (U32 length) +{ + U32 res = mmcSendCmd (CMD16, length); + spiChipSelect (0); + return (res); +} + +/************************************************************************* + * Function Name: mmcCSDImplement + * Parameters: none + * + * Return: none + * + * Description: Implemet data from CSD + * + *************************************************************************/ +static void mmcCSDImplement (void) +{ + U32 frequency; + + // + // Calculate SPI max clock + // + frequency = mmcTransfExp [CSD_GET_TRAN_SPEED_EXP ()] * mmcCsdMant [CSD_GET_TRAN_SPEED_MANT ()]; + + if (frequency > 20000000) + frequency = 20000000; + + frequency = spiSetClockFreq (frequency); + + if (mmcDskStatus.DiskType == DiskMMC) + { + Tnac = mmmcAccessTime [CSD_GET_TAAC_EXP ()] * mmcAccessTimeMant [CSD_GET_TAAC_MANT ()]; + Tnac = frequency / Tnac; + Tnac += 1 << (CSD_GET_NSAC () + 4); + Tnac *= 10; + Twr = Tnac * CSD_GET_R2W_FACTOR (); + } + else + { + Tnac = frequency / SD_READ_TIME_OUT; + Twr = frequency / SD_WRITE_TIME_OUT; + } + + mmcDskStatus.BlockSize = 1 << CSD_GET_READ_BL_LEN (); + mmcDskStatus.BlockNumb = (CSD_GET_C_SIZE () + 1) * (4 << CSD_GET_C_SIZE_MULT ()); + mmcDskStatus.WriteProtect = spiWriteProtect () | CSD_GET_PERM_WRITE_PROTECT () | CSD_GET_TMP_WRITE_PROTECT (); +} + +/************************************************************************* + * Function Name: mmcInitMedia + * Parameters: none + * + * Return: mmcState_t + * + * Description: MMC detect and initialize + * + *************************************************************************/ +static mmcState_t mmcInitMedia (void) +{ + U32 i; + U32 res; + + Tnac = 1; + + if (!spiPresent ()) + return (MmcNoPresent); + + // + // Clock Freq. Identification Mode < 400kHz + // + spiSetClockFreq (IdentificationModeClock); + + // + // Set maximum time out + // + Tnac = IdentificationModeClock / SD_READ_TIME_OUT; + + // + // Power up cycles. After power up at least 74 clock cycles are required + // prior to starting bus communication + // + for (i = 0; i < 2; i++) + { + spiChipSelect (0); + + for (res = 10; res; --res) + spiTransferByte (0xff); + + // + // CMD0 (Go to IDLE) to put MMC in SPI mode + // + res = mmcSendCmd (CMD0, 0); + spiChipSelect (0); + + if (res == MMC_IDLE_STATE) + break; + } + + if (res != MMC_IDLE_STATE) + return (MmcNoResponse); + + // + // Determinate Card type SD or MMC + // + mmcDskStatus.DiskType = DiskMMC; + + for (i = 100; i; --i) + { + spiChipSelect (0); + spiTransferByte (0xff); + res = mmcSendCmd (CMD55, 0); + spiChipSelect (0); + spiChipSelect (0); + spiTransferByte (0xff); + res = mmcSendCmd (ACMD41, 0); + spiChipSelect (0); + + if (res & MMC_ILLEGAL_CMD) + { + // + // MMC card may be CMD1 for MMC Init sequence will be complete within 500ms + // + for (i = 100; i; --i) + { + spiChipSelect (0); + spiTransferByte (0xff); + res = mmcSendCmd (CMD1, 0); + spiChipSelect (0); + + if (res == MMC_OK) + break; + + spiDelay1ms (50); + } + break; + } + + if (res == MMC_OK) + { + mmcDskStatus.DiskType = DiskSD; + break; + } + + spiDelay1ms (50); + } + + if (i == 0) + return (MmcNoResponse); + + // + // Read CSD. CSD must always be valid + // + if ((res = mmcReadCardInfo (mmcSdCsd, CMD9)) != MmcOk) + return (MmcNoResponse); + + // + // Implement CSD data, and set block size + // + mmcCSDImplement (); + mmcSetBlockLen (mmcDskStatus.BlockSize); + + // mmcDecode (mmcSdCsd); // ### + + return (MmcOk); +} + +/************************************************************************* + * Function Name: mmcReadCardInfo + * Parameters: U8 *pData, + * mmcSpiCmdInd_t Command - CMD9, CMD10 are only allowed + * + * Return: mmcState_t + * + * Description: Read CSD or CID registers depend of commoand + * + *************************************************************************/ +static mmcState_t mmcReadCardInfo (U8 *pData, mmcSpiCmdInd_t Command) +{ + U32 i; + U32 res; + + switch (Command) + { + case CMD9 : + case CMD10 : + break; + + default: + return (MmmcParameterError); + } + + spiChipSelect (0); + spiTransferByte (0xff); + + if ((res = mmcSendCmd (Command, 0)) == MMC_OK) + { + for (i = 8; i ; --i) + { + if (((res = spiTransferByte (0xff)) | MMC_DATA_ERR_TOKEN) == MMC_DATA_ERR_TOKEN) + { + // printf ("line %d: spiTransferByte returned 0x%x\n", __LINE__, res); // ### + MMC_RET_DATA_ERR (res); + } + else if (res == MMC_DATA_TOKEN) + { + for (i = 0; i <16 ; ++i) + *pData++ = spiTransferByte (0xff); + + // + // CRC receive + // + spiTransferByte (0xff); + spiTransferByte (0xff); + spiChipSelect (0); + return (MmcOk); + } + } + } + // else + // printf ("line %d: mmcSendCmd returned %d\n", __LINE__, res); // ### + + spiChipSelect (0); + MMC_RET_ERROR (res); +} + +/************************************************************************* + * Function Name: mmcRead + * Parameters: U8 *pData, U32 Add, U32 Length + * + * Return: mmcState_t + * + * Description: Read from a MMC + * + *************************************************************************/ +static inline mmcState_t mmcRead (U8 *pData, U32 Add, U32 Length) +{ + U32 res; + U32 i; + + // + // For synchronization + // + spiChipSelect (0); + spiTransferByte (0xff); + res = mmcSendCmd (CMD17, Add); + + if (res == MMC_OK) + { + for (i = Tnac; i; --i) + { + res = spiTransferByte (0xff); + + if ((res | MMC_DATA_ERR_TOKEN) == MMC_DATA_ERR_TOKEN) + MMC_RET_DATA_ERR (res); + else if (res == MMC_DATA_TOKEN) + { + spiReceiveBlock (pData, Length); + + // + // CRC receive + // + spiTransferByte (0xff); + spiTransferByte (0xff); + spiChipSelect (0); + return (MmcOk); + } + } + + spiChipSelect (0); + return (MmcNoResponse); + } + + spiChipSelect (0); + MMC_RET_ERROR (res); +} + +/************************************************************************* + * Function Name: mmcWrite + * Parameters: U8 *pData, U32 Add, U32 Length + * + * Return: mmcState_t + * + * Description: Write to a MMC + * + *************************************************************************/ +static inline mmcState_t mmcWrite (U8 *pData, U32 Add, U32 Length) +{ + U32 res; + U32 i; + + // + // For synchronization + // + spiChipSelect (0); + spiTransferByte (0xff); + + if ((res = mmcSendCmd (CMD24, Add)) == MMC_OK) + { + spiTransferByte (0xff); + spiTransferByte (MMC_DATA_TOKEN); + spiSendBlock (pData, Length); + + // + // CRC Send + // + spiTransferByte (0xff); + spiTransferByte (0xff); + + if ((spiTransferByte (0xff) & 0x1F) != 0x05) + MMC_RET_ERROR (res); + + for (i = Twr; i ;i--) + if (spiTransferByte (0xff) == 0xff) + break; + + spiChipSelect (0); + + if (i == 0) + return (MmcNoResponse); + + return (MmcOk); + } + + spiChipSelect (0); + MMC_RET_ERROR (res); +} + +/************************************************************************* + * Function Name: mmcVerify + * Parameters: U8 *pData, U32 Add, U32 Length + * + * Return: mmcState_t + * + * Description: Verify on a MMC + * + *************************************************************************/ +static inline mmcState_t mmcVerify (U8 *pData, U32 Add, U32 Length) +{ + U32 res,i; + + // + // For synchronization + // + spiChipSelect (0); + spiTransferByte (0xff); + + if ((res = mmcSendCmd (CMD17, Add)) == MMC_OK) + { + for (i = Tnac; i; --i) + { + res = spiTransferByte (0xff); + + if ((res | MMC_DATA_ERR_TOKEN) == MMC_DATA_ERR_TOKEN) + MMC_RET_DATA_ERR (res); + else if (res == MMC_DATA_TOKEN) + { + for (res = 0, i = 0; i < Length; ++i, ++pData) + { + *pData ^= spiTransferByte (0xff); + + if (*pData != 0) + res = 1; + } + + // + // CRC receive + // + spiTransferByte (0xff); + spiTransferByte (0xff); + spiChipSelect (0); + spiTransferByte (0xff); + spiTransferByte (0xff); + + if (res) + return (MmcMiscompare); + + return (MmcOk); + } + } + + return (MmcNoResponse); + } + + MMC_RET_ERROR (res); +} + +// +// +// +DSTATUS diskInitialize (U8 drv __attribute__ ((unused))) +{ + mmcDskStatus.BlockNumb = mmcDskStatus.BlockSize = mmcLastError = 0; + + // + // Init SPI + // + spiInit (); + + // + // Media Init + // + switch (mmcInitMedia ()) + { + case MmcOk : + { + mmcCSDImplement (); + mmcDskStatus.DiskStatus = DiskCommandPass; + mmcDskStatus.MediaChanged = TRUE; + Stat = 0; + if (mmcDskStatus.WriteProtect) + Stat |= STA_PROTECT; + } + break; + + case MmcCardError : + case MmcDataError : + { + mmcDskStatus.DiskStatus = DiskNotReady; + Stat = STA_NOINIT; + if (mmcDskStatus.WriteProtect) + Stat |= STA_PROTECT; + } + break; + + default: + { + mmcDskStatus.DiskStatus = DiskNotPresent; + Stat = STA_NODISK; + } + break; + } + + return Stat; +} + +// +// +// +DSTATUS diskShutdown (void) +{ + Stat |= STA_NOINIT; + + return Stat; +} + +// +// +// +DSTATUS diskStatus (U8 drv __attribute__ ((unused))) +{ + return Stat; +} + +// +// +// +DRESULT diskRead (U8 disk __attribute__ ((unused)), U8 *buff, U32 sector, U8 count) +{ + U32 res; + + if (Stat & STA_NOINIT) + return RES_NOTRDY; + if (!count) + return RES_PARERR; + + res = mmcRead (buff, sector * mmcDskStatus.BlockSize, count * mmcDskStatus.BlockSize); + + if (res == MMC_OK) + return RES_OK; + else + return RES_ERROR; +} + +// +// +// +#if _FS_READONLY == 0 +DRESULT diskWrite (U8 disk __attribute__ ((unused)), const U8 *buff, U32 sector, U8 count) +{ + U32 res; + + if (Stat & STA_NOINIT) + return RES_NOTRDY; + if (Stat & STA_PROTECT) + return RES_WRPRT; + if (!count) + return RES_PARERR; + + res = mmcWrite ((U8 *) buff, sector * mmcDskStatus.BlockSize, count * mmcDskStatus.BlockSize); + + if (res == MMC_OK) + return RES_OK; + else + return RES_ERROR; +} +#endif + +// +// +// +DRESULT diskIoctl (U8 drv, U8 ctrl, void *buff) +{ + DRESULT res; + U8 n; + U8 csd [16]; + U16 csize; + + if (drv) + return RES_PARERR; + if (Stat & STA_NOINIT) + return RES_NOTRDY; + + res = RES_ERROR; + + switch (ctrl) + { + case GET_SECTOR_COUNT : + { + mmcState_t jcwres; // ### + if ((jcwres = mmcReadCardInfo (csd, CMD9)) == MmcOk) + { + // + // SDC ver 2.00 + // + if ((csd [0] >> 6) == 1) + { + csize = csd [9] + ((U16)csd [8] << 8) + 1; + *(U32 *) buff = (U32) csize << 10; + } + // + // MMC or SDC ver 1.XX + // + else + { + n = (csd [5] & 15) + ((csd [10] & 128) >> 7) + ((csd [9] & 3) << 1) + 2; + csize = (csd [8] >> 6) + ((U16) csd [7] << 2) + ((U16) (csd [6] & 3) << 10) + 1; + *(U32 *) buff = (U32) csize << (n - 9); + } + + res = RES_OK; + } + // else // ### + // printf ("line %d: mmcReadCardInfo returned %d\n", __LINE__, jcwres); // ### + } + break; + + case GET_SECTOR_SIZE : + { + *(U16 *) buff = 512; + res = RES_OK; + } + break; + + case CTRL_SYNC : + { + if (spiWaitReady () == 0xff) + res = RES_OK; + } + break; + + case MMC_GET_CSD : + { + if (mmcReadCardInfo (buff, CMD9) == MmcOk) + res = RES_OK; + } + break; + + case MMC_GET_CID : + { + if (mmcReadCardInfo (buff, CMD10) == MmcOk) + res = RES_OK; + } + break; + +# if 0 + case CTRL_POWER: + case CTRL_LOCK: + case CTRL_EJECT: + case MMC_GET_OCR: /* Receive OCR as an R3 resp (4 bytes) */ + case ATA_GET_REV: + case ATA_GET_MODEL: + case ATA_GET_SN: +#endif + + default: + res = RES_PARERR; + } + + return res; +} + +#if 0 +static void mmcDump (U8 *buff, U32 ofs, U8 cnt) +{ + U8 n; + + printf ("\n\r%08X ", ofs); + + for (n = 0; n < cnt; n++) + printf (" %02X", buff [n]); + + putchar (' '); + + for (n = 0; n < cnt; n++) + { + if ((buff [n] < 0x20) || (buff [n] >= 0x7F)) + putchar ('.'); + else + putchar (buff [n]); + } +} +#endif + +#if 0 +typedef struct csd_11_s +{ + unsigned int not_used_1 : 2; // 0..1 [121:120] 0x44 + unsigned int mmc_prot : 4; // 2..5 [125:122] + unsigned int csd_structure : 2; // 6..7 [127:126] + unsigned int taac : 8; // 0..7 [119:112] 0x26 + unsigned int nsac : 8; // 0..7 [111:104] 0x00 + unsigned int tran_speed : 8; // 0..7 [103:96] 0x2a + unsigned int ccc_hi : 8; // 0..7 [95:88] 0x1f + unsigned int read_bl_len : 4; // 0..3 [83:80] 0xf9 + unsigned int ccc_lo : 4; // 4..7 [87:84] + + unsigned int c_size_hi : 2; // 0..1 [73:72] 0x83 + unsigned int not_used_2 : 2; // 2..3 [75:74] + unsigned int dsr_imp : 1; // 4..4 [76:76] + unsigned int read_blk_misalign : 1; // 5..5 [77:77] + unsigned int write_blk_misalign : 1; // 6..6 [78:78] + unsigned int read_bl_partial : 1; // 7..7 [79:79] + unsigned int c_size_mid : 8; // 0..7 [71:64] 0xd3 + unsigned int vdd_r_curr_max : 3; // 0..1 [58:56] 0xe3 + unsigned int vdd_r_curr_min : 3; // 2..5 [61:59] + unsigned int c_size_lo : 2; // 6..7 [63:62] + + unsigned int c_size_mult_hi : 2; // 0..1 [49:48] 0x91 + unsigned int vdd_w_curr_max : 3; // 2..4 [52:50] + unsigned int vdd_w_curr_min : 3; // 4..7 [55:53] + unsigned int erase_grp_size_lo : 2; // 0..1 [41:40] 0x83 + unsigned int sector_size : 5; // 2..6 [46:42] + unsigned int c_size_mult_lo : 1; // 7..7 [47:47] + unsigned int wp_grp_size : 5; // 0..4 [36:32] 0xff + unsigned int erase_grp_size_hi : 3; // 5..7 [39:37] + + unsigned int write_bl_len_hi : 2; // 0..1 [25:24] 0x92 + unsigned int r2w_factor : 3; // 2..4 [28:26] + unsigned int default_ecc : 2; // 5..6 [30:29] + unsigned int wp_grp_enable : 1; // 7..7 [31:31] + unsigned int not_used_3 : 5; // 0..4 [20:16] 0x40 + unsigned int write_bl_partial : 1; // 5..5 [21:21] + unsigned int write_bl_len_lo : 2; // 6..7 [23:22] + + unsigned int ecc : 2; // 0..1 [9:8] 0x40 + unsigned int not_used_5 : 2; // 2..3 [11:10] + unsigned int tmp_write_protect : 1; // 4..4 [12:12] + unsigned int perm_write_protect : 1; // 5..5 [13:13] + unsigned int copy : 1; // 6..6 [14:14] + unsigned int not_used_4 : 1; // 7..7 [15:15] + + unsigned int notused_6 : 1; // 0..0 [0:0] 0x67 + unsigned int crc : 7; // 1..7 [7:1] +} +__attribute__ ((packed)) csd_11_t; + +static void mmcDecode (U8 *buffer) +{ + csd_11_t *p = (csd_11_t *) buffer; + + printf ("\n"); + printf ("sizeof (csd_11_t) : %lu\n", sizeof (csd_11_t)); + printf ("CSD structure version : 1.%d\n", p->csd_structure); + printf ("MMC protocol version : %d\n", p->mmc_prot); + printf ("TAAC : 0x%02x\n", p->taac); + printf ("NSAC : 0x%02x\n", p->nsac); + printf ("TRAN_SPEED : 0x%02x\n", p->tran_speed); + printf ("CCC : 0x%03x\n", (p->ccc_hi << 4) | p->ccc_lo); + printf ("READ_BL_LEN : %d\n", p->read_bl_len); + printf ("READ_BL_PARTIAL : %d\n", p->read_bl_partial); + printf ("WRITE_BLK_MISALIGN : %d\n", p->write_blk_misalign); + printf ("READ_BLK_MISALIGN : %d\n", p->read_blk_misalign); + printf ("DSR_IMP : %d\n", p->read_blk_misalign); + printf ("C_SIZE : %d\n", (p->c_size_hi << 10) | (p->c_size_mid << 2) | p->c_size_lo); + printf ("VDD_R_CURR_MIN : %d\n", p->vdd_r_curr_min); + printf ("VDD_R_CURR_MAX : %d\n", p->vdd_r_curr_max); + printf ("VDD_W_CURR_MIN : %d\n", p->vdd_w_curr_min); + printf ("VDD_W_CURR_MAX : %d\n", p->vdd_w_curr_max); + printf ("VDD_W_CURR_MAX : %d\n", p->vdd_w_curr_max); + printf ("C_SIZE_MULT : %d\n", (p->c_size_mult_hi << 1) | p->c_size_mult_lo); + printf ("SECTOR_SIZE : %d\n", p->sector_size); + printf ("ERASE_GRP_SIZE : %d\n", (p->erase_grp_size_hi << 2) | p->erase_grp_size_lo); + printf ("WP_GRP_SIZE : %d\n", p->wp_grp_size); + printf ("WP_GRP_ENABLE : %d\n", p->wp_grp_enable); + printf ("DEFAULT_ECC : %d\n", p->default_ecc); + printf ("R2W_FACTOR : %d\n", p->r2w_factor); + printf ("WRITE_BL_LEN : %d\n", (p->write_bl_len_hi << 2) | p->write_bl_len_lo); + printf ("WRITE_BL_PARTIAL : %d\n", p->write_bl_partial); + printf ("COPY : %d\n", p->copy); + printf ("PERM_WRITE_PROTECT : %d\n", p->perm_write_protect); + printf ("TMP_WRITE_PROTECT : %d\n", p->tmp_write_protect); + printf ("ECC : %d\n", p->ecc); + printf ("MEDIA SIZE : %u\n", (U32) (((p->c_size_hi << 10) | (p->c_size_mid << 2) | p->c_size_lo) + 1) * + (U32) (4 << ((p->c_size_mult_hi << 1) | p->c_size_mult_lo)) * + (U32) (1 << (p->read_bl_len))); +} +#endif + diff --git a/fatfs/mmc.h b/fatfs/mmc.h new file mode 100644 index 0000000..9d61c85 --- /dev/null +++ b/fatfs/mmc.h @@ -0,0 +1,127 @@ +/************************************************************************* + * + * Used with ICCARM and AARM. + * + * (c) Copyright IAR Systems 2003 + * + * File name : mmc.h + * Description : define MMC module + * + * History : + * 1. Data : July 1, 2005 + * Author : Stanimir Bonev + * Description : Create + * + * $Revision: 1.4 $ +**************************************************************************/ + +#ifndef _MMC_H_ +#define _MMC_H_ + +#include "sysdefs.h" +#include "disk.h" + +// +// Hardware depends definitions +// +#define IdentificationModeClock 400000l +#define SystemPeripherialClock 48000000l + +// +// Time out definition for SD Read Time out ~100msec, Write Time out ~250ms +// +#define SD_READ_TIME_OUT 7 // [Hz] +#define SD_WRITE_TIME_OUT 3 // [HZ] + +// +// +// +#define MMC_OK 0x00 +#define MMC_IDLE_STATE 0x01 +#define MMC_ERASE_RST 0x02 +#define MMC_ILLEGAL_CMD 0x04 +#define MMC_CRC_ERROR 0x08 +#define MMC_ERASE_ERROR 0x10 +#define MMC_ADD_ERROR 0x20 +#define MMC_PARAM_ERROR 0x40 + +#define MMC_DATA_TOKEN 0xfe +#define MMC_DATA_ERR_TOKEN 0x1f +#define MMC_STOP_TRAN 0xfd + +#define MMC_DLY_1MSEC 1000 + +typedef enum +{ + MmcOk = 0, + MmcNoPresent, + MmcNoResponse, + MmcCardError, + MmcDataError, + MmcUknowError, + MmmcParameterError, + MmcMiscompare +} +mmcState_t; + +typedef enum +{ + CMD0 = 0, // Resets the MultiMediaCard + CMD1, // Activates the card initialization process + CMD9, // Asks the selected card to send its card-specific data (CSD) + CMD10, // Asks the selected card to send its card identification (CID) + CMD12, // Stop transmission on multiple block read + CMD13, // Asks the selected card to send its status register + CMD16, // Selects a block length (in bytes) for all following block commands (read and write) + CMD17, // Reads a block of the size selected by the SET_BLOCKLEN command + CMD18, // Continuously transfers data blocks from card to host until interrupted by a Stop command or the requested number of data blocks transmitted + CMD24, // Writes a block of the size selected by the SET_BLOCKLEN command + CMD25, // Continuously writes blocks of data until a "Stop Tran" token or the requested number of blocks received + CMD27, // Programming of the programmable bits of the CSD + CMD28, // If the card has write protection features, this command sets the write protection bit of the addressed group. The properties of write protection are coded in the card specific data (WP_GRP_SIZE). + CMD29, // If the card has write protection features, this command clears the write protection bit of the addressed group + CMD30, // If the card has write protection features, this command asks the card to send the status of the write protection bits + CMD32, // Sets the address of the first sector of the erase group + CMD33, // Sets the address of the last sector in a continuous range within the selected erase group, or the address of a single sector to be selected for erase + CMD34, // Removes one previously selected sector from the erase selection + CMD35, // Sets the address of the first erase group within a range to be selected for erase + CMD36, // Sets the address of the last erase group within a continuous range to be selected for erase + CMD37, // Removes one previously selected erase group from the erase selection. + CMD38, // Erases all previously selected sectors + CMD42, // Used to set/reset the password or lock/unlock the card. The size of the Data Block is defined by the SET_BLOCK_LEN command + CMD55, // Notifies the card that the next command is an application specific command rather than a standard command. + CMD56, // Used either to transfer a Data Block to the card or to get a Data Block from the card for general purpose/application specific commands. The size of the Data Block is defined with SET_BLOCK_LEN command + CMD58, // Reads the OCR register of a card + CMD59, // Turns the CRC option on or off. A ‘1Â’ in the CRC option bit will turn the option on, a ‘0Â’ will turn it off + ACMD41, // Activates the cardÂ’s initialization process (Only for SD) + CMD_END // End of commands index +} +mmcSpiCmdInd_t; + +typedef enum +{ + MmcNoArg = 0, + MmcBlockLen, + MmcDataAdd, + MmcDummyWord +} +mmcAgmType_t; + +typedef enum +{ + MmcR1 = 0, + MmcR1b, + MmcR2, + MmcR3 +} +mmcRespType_t; + +typedef struct +{ + U8 TxData; + mmcAgmType_t Arg; + mmcRespType_t Resp; +} +mmcCommads_t; + +#endif diff --git a/fatfs/spi.c b/fatfs/spi.c new file mode 100644 index 0000000..d478381 --- /dev/null +++ b/fatfs/spi.c @@ -0,0 +1,284 @@ +#include // ### +#include "lpc210x.h" +#include "mmc.h" +#include "spi.h" + +/************************************************************************* + * + * Used with ICCARM and AARM. + * + * (c) Copyright IAR Systems 2003 + * + * File name : mmc_ll.h + * Description : define MMC module + * + * History : + * 1. Data : July 1, 2005 + * Author : Stanimir Bonev + * Description : Create + * 2. Data : July 1, 2005 + * Author : Stanimir Bonev + * Description : Modify + * Fix a lock problem in spiReceiveBlock + * + * $Revision: 1.4 $ + * (C) Joel Winarske, 2006,2007 +**************************************************************************/ + +/************************************************************************* + * Function Name: spiChipSelect + * Parameters: Boolean Select + * Return: none + * + * Description: MMC Chip select control + * Select = true - Chip is enabled + * Select = false - Chip is disabled + * + *************************************************************************/ +void spiChipSelect (BOOL select) +{ + if (select) + GPIO0_IOCLR = GPIO_IO_P20; + else + { + GPIO0_IOSET = GPIO_IO_P20; + + while (!(SSP_SR & SSP_SR_TNF)) + ; + + SSP_DR = 0xff; + + // + // Wait until TX fifo and TX shift buffer are empty + // + while (SSP_SR & SSP_SR_BSY) + ; + while (!(SSP_SR & SSP_SR_RNE)) + ; + + do + { + select = SSP_DR; + } + while (SSP_SR & SSP_SR_RNE); + } +} + +/************************************************************************* + * Function Name: spiPresent + * Parameters: none + * Return: Boolean - true card present + * - false card not present + * + * Description: MMC present check + * + *************************************************************************/ +inline BOOL spiPresent (void) +{ + return TRUE; +} + +/************************************************************************* + * Function Name: spiWriteProtect + * Parameters: none + * Return: Boolean - true card is protected + * - false card not protected + * + * Description: MMC Write protect check + * + *************************************************************************/ +inline BOOL spiWriteProtect (void) +{ + return FALSE; +} + +/************************************************************************* + * Function Name: spiSetClockFreq + * Parameters: Int32U Frequency + * Return: Int32U + * + * Description: Set SPI ckl frequency + * + *************************************************************************/ +U32 spiSetClockFreq (U32 frequency) +{ + U32 Div = SystemPeripherialClock / frequency; + + if (Div < 2) + Div = 2; + else if (Div > 254) + Div = 254; + + ++Div; + Div &= ~1; + SSP_CPSR = Div; + + return (SystemPeripherialClock / Div); +} + +/************************************************************************* + * Function Name: spiInit + * Parameters: none + * Return: int + * 0 - no error + * 1 - speed is to high + * + * Description: Init SPI, Cart Present, Write Protect and Chip select pins + * + *************************************************************************/ +void spiInit (void) +{ + U32 i; + volatile U32 dummy; + + // + // Chip select + // + GPIO0_IOSET = GPIO_IO_P20; + GPIO0_IODIR |= GPIO_IO_P20; + + // + // SPI init + // + SCB_PCONP |= SCB_PCONP_PCSPI1; + + SSP_CR0 = SSP_CR0_DSS_8 | SSP_CR0_FRF_SPI; + SSP_CR1 = 0x00; + SSP_IMSC = 0x00; + + // + // Clock Freq. Identification Mode < 400kHz + // + spiSetClockFreq (400000); + + PCB_PINSEL1 |= PCB_PINSEL1_P017_SCK1; + PCB_PINSEL1 |= PCB_PINSEL1_P018_MISO1; + PCB_PINSEL1 |= PCB_PINSEL1_P019_MOSI1; + PCB_PINSEL1 &= ~PCB_PINSEL1_P020_MASK; + + // + // Enable SPI + // + SSP_CR1 |= SSP_CR1_SSE; + + for (i = 0; i < 8; i++) + dummy = SSP_DR; +} + +/************************************************************************* + * Function Name: spiTransferByte + * Parameters: U8 ch + * Return: U8 + * + * Description: Read byte from SPI + * + *************************************************************************/ +U8 spiTransferByte (U8 c) +{ + while (!(SSP_SR & SSP_SR_TNF)) + ; + + SSP_DR = c; + + while (!(SSP_SR & SSP_SR_RNE)) + ; + + return SSP_DR; +} + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ +U8 spiWaitReady (void) +{ + U8 res; + + do /* TODO: add in 500ms timeout */ + { + SSP_DR = 0xff; + + while (!(SSP_SR & SSP_SR_RNE)) + ; + + res = SSP_DR; + } + while (res != 0xff); + + return res; +} + +/************************************************************************* + * Function Name: spiSendBlock + * Parameters: pInt8U pData, Int32U Size + * + * Return: void + * + * Description: Read byte from SPI + * + *************************************************************************/ +void spiSendBlock (U8 *pData, U32 size) +{ + U32 outCount = size; + volatile U32 dummy; + + while (outCount) + { + while ((SSP_SR & SSP_SR_TNF) && outCount) + { + SSP_DR = *pData++; + --outCount; + } + } + + while ((SSP_SR & SSP_SR_RNE) || !(SSP_SR & SSP_SR_TFE)) + dummy = SSP_DR; +} + +/************************************************************************* + * Function Name: spiReceiveBlock + * Parameters: pInt8U pData, Int32U Size + * + * Return: void + * + * Description: Read byte from SPI + * + *************************************************************************/ +void spiReceiveBlock (U8 *pData, U32 size) +{ + U32 delta = 0; + + while (size || delta) + { + while ((SSP_SR & SSP_SR_TNF) && (delta < SSP_FIFO_DEPTH) && size) + { + SSP_DR = 0xff; + --size; + ++delta; + } + + while (SSP_SR & SSP_SR_RNE) + { + *pData++ = SSP_DR; + --delta; + } + } +} + +/************************************************************************* + * Function Name: spiDly_1ms + * Parameters: Int32U Delay + * Return: none + * + * Description: Delay [msec] + * + *************************************************************************/ +void spiDelay1ms (U32 delay) +{ + volatile U32 i; + + for (;delay; --delay) + { + for (i = MMC_DLY_1MSEC; i; --i) + ; + } +} diff --git a/fatfs/spi.h b/fatfs/spi.h new file mode 100644 index 0000000..a2a6eaa --- /dev/null +++ b/fatfs/spi.h @@ -0,0 +1,20 @@ +#ifndef _MMCLLSPI1_H_ +#define _MMCLLSPI1_H_ + +#include "sysdefs.h" + +// +// +// +void spiChipSelect (BOOL select); +inline BOOL spiPresent (void); +inline BOOL spiWriteProtect (void); +U32 spiSetClockFreq (U32 frequency); +void spiInit (void); +U8 spiTransferByte (U8 c); +U8 spiWaitReady (void); +void spiSendBlock (U8 *pData, U32 size); +void spiReceiveBlock (U8 *pData, U32 size); +void spiDelay1ms (U32 delay); + +#endif diff --git a/fiq/Makefile b/fiq/Makefile new file mode 100644 index 0000000..53e74ae --- /dev/null +++ b/fiq/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=fiq.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/fiq/fiq.c b/fiq/fiq.c new file mode 100644 index 0000000..71a0d99 --- /dev/null +++ b/fiq/fiq.c @@ -0,0 +1,59 @@ +#include "FreeRTOS.h" + +#include "fiq.h" + +// +// +// +static volatile unsigned int fiqCounter; + +// +// +// +void fiqInit (void) +{ + SCB_PCONP |= SCB_PCONP_PCTIM1; + + VIC_IntSelect |= VIC_IntSelect_Timer1; + VIC_IntEnable = VIC_IntEnable_Timer1; + + T1_PR = 0; + T1_MR0 = configCPU_CLOCK_HZ / 8; + T1_MCR = T_MCR_MR0R | T_MCR_MR0I; +} + +int fiqEnable (void) +{ + unsigned int state = T1_TCR; + + T1_TCR = T_TCR_CE; + + return (state & T_TCR_CE) ? 1 : 0; +} + +int fiqDisable (void) +{ + unsigned int state = T1_TCR; + + T1_TCR = T_TCR_CR; + + return (state & T_TCR_CE) ? 1 : 0; +} + +unsigned int fiqGetCount (void) +{ + return fiqCounter; +} + +void fiqClearCount (void) +{ + fiqCounter = 0; +} + +void fiqISR (void) __attribute__ ((interrupt ("FIQ"))); +void fiqISR (void) +{ + ++fiqCounter; + + T1_IR = T_IR_MASK; +} diff --git a/fiq/fiq.h b/fiq/fiq.h new file mode 100644 index 0000000..cc16aef --- /dev/null +++ b/fiq/fiq.h @@ -0,0 +1,11 @@ +#ifndef _FIQ_H_ +#define _FIQ_H_ + +void fiqInit (void); +int fiqEnable (void); +int fiqDisable (void); +unsigned int fiqGetCount (void); +void fiqClearCount (void); +void fiqISR (void); + +#endif diff --git a/gps/Makefile b/gps/Makefile new file mode 100644 index 0000000..1c20afa --- /dev/null +++ b/gps/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=gps.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/gps/gps.c b/gps/gps.c new file mode 100644 index 0000000..c0a9f1c --- /dev/null +++ b/gps/gps.c @@ -0,0 +1,284 @@ +// +// +// +#include +#include +#include +#include + +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +#include "gps.h" + +// +// +// +#define GPS_MAX_NMEA_SENTENCE 128 + +// +// +// +static gpsData_t gpsData; +static xSemaphoreHandle semaphore; + +// +// +// +static void gpsNormalizeNMEA (char *s); +static unsigned char gpsChecksumNMEA (char *sz); +static void gpsHandlerGGA (char *nmeaSentence); +static void gpsHandlerRMC (char *nmeaSentence); +static void gpsHandlerRestart (char *nmeaSentence); +static void gpsDispatchMessages (char *nmeaSentence); +static int gpsProcessByte (unsigned char c, char *nmeaSentence); + +// +// +// +static void gpsNormalizeNMEA (char *s) +{ + int l = -1; + + while (*s && *s != '*') + { + if (*s == ',' && (*(s + 1) == ',' || *(s + 1) == '*')) + { + if (l == -1) + l = strlen (s) + 1; + + memmove (s + 3, s + 1, l); + *++s = '-'; + *++s = '1'; + l += 2; + } + + s++; + + if (l != -1) + l--; + } +} + +// +// +// +static unsigned char gpsChecksumNMEA (char *sz) +{ + short i; + unsigned char cs; + + for (cs = 0, i = 1; sz [i] && sz [i] != '*'; i++) + cs ^= ((unsigned char) sz [i]); + + return cs; +} + +// +// +// +static void gpsHandlerGGA (char *nmeaSentence) +{ + char valid = 0; + double height = 0.0; + double latitude = 0.0, longitude = 0.0; + char latitudeSign = 0, longitudeSign = 0; + + if (sscanf (nmeaSentence, "%*f,%lf,%c,%lf,%c,%c,%*d,%*f,%lf", &latitude, &latitudeSign, &longitude, &longitudeSign, &valid, &height) == 6) + { + if (xSemaphoreTake (semaphore, portMAX_DELAY) == pdTRUE) + { + gpsData.valid = (valid - '0'); + gpsData.height = height; + gpsData.latitude = latitude * (latitudeSign == 'N' ? 1.0 : -1.0); + gpsData.longitude = longitude * (longitudeSign == 'E' ? 1.0 : -1.0); + + xSemaphoreGive (semaphore); + } + } +} + +static void gpsHandlerRMC (char *nmeaSentence) +{ + float speed, course; + int gpsdate, gpstime; + + if (sscanf (nmeaSentence, "%d.%*d,%*c,%*f,%*c,%*f,%*c,%f,%f,%d", &gpstime, &speed, &course, &gpsdate) == 4) + { + if (xSemaphoreTake (semaphore, portMAX_DELAY) == pdTRUE) + { + gpsData.utcDay = gpsdate / 10000; + gpsData.utcMonth = (gpsdate / 100) % 100; + gpsData.utcYear = (gpsdate % 100) + 2000; + gpsData.utcHours = gpstime / 10000; + gpsData.utcMinutes = (gpstime / 100) % 100; + gpsData.utcSeconds = gpstime % 100; + gpsData.groundSpeed = speed; + gpsData.trueCourse = course; + + xSemaphoreGive (semaphore); + } + } +} + +static void gpsHandlerRestart (char *nmeaSentencemea __attribute__ ((unused))) +{ + if (xSemaphoreTake (semaphore, portMAX_DELAY) == pdTRUE) + { + gpsData.restarts++; + xSemaphoreGive (semaphore); + } +} + +// +// +// +typedef struct nmeaDispatch_s +{ + const char *sentence; + short length; + void (*handler) (char *sentence); + short normalize; +} +nmeaDispatch_t; + +static const nmeaDispatch_t nmeaDispatch [] = +{ + { "$GPGGA", 6, gpsHandlerGGA, 1 }, + { "$GPRMC", 6, gpsHandlerRMC, 1 }, + { "$HW Type", 8, gpsHandlerRestart, 0 }, + { NULL, 0, NULL, 0 } +}; + +static void gpsDispatchMessages (char *nmeaSentence) +{ + int i; + + for (i = 0; nmeaDispatch [i].handler; i++) + { + if (!strncmp (nmeaDispatch [i].sentence, nmeaSentence, nmeaDispatch [i].length)) + { + if (nmeaDispatch [i].normalize) + { + gpsNormalizeNMEA (&nmeaSentence [7]); + (*nmeaDispatch [i].handler) (&nmeaSentence [7]); + } + else + (*nmeaDispatch [i].handler) (NULL); + + break; + } + } +} + +// +// +// +static int gpsProcessByte (unsigned char c, char *nmeaSentence) +{ + short complete = 0; + static short state = 0; + static short pos = 0; + + switch (state) + { + case 0 : + { + if (c == '$') + { + pos = 0; + nmeaSentence [pos++] = '$'; + nmeaSentence [pos] = '\0'; + state = 1; + } + else + state = 0; + } + break; + + case 1 : + { + if (pos < GPS_MAX_NMEA_SENTENCE) + { + if (c == 0x0a) + { + char *s; + + state = 0; + + if ((s = strchr (nmeaSentence, '*'))) + { + int cksum; + + if (sscanf (s + 1, "%x", &cksum) == 1) + { + if (gpsChecksumNMEA (nmeaSentence) == cksum) + complete = 1; + } + else + fprintf (stderr, "NMEA checksum error: got 0x%02x, want %s", cksum, s); + } + else + fprintf (stderr, "NMEA checksum not found: \"%s\"", nmeaSentence); + } + else if (c != 0x0d) + { + nmeaSentence [pos++] = c; + nmeaSentence [pos] = '\0'; + } + } + else + state = 0; + } + break; + } + + return (complete); +} + +// +// Return 1 if got a copy, 0 if not. +// +int gpsCopyData (gpsData_t *dst) +{ +#ifndef CFG_CONSOLE_UART1 + if (xSemaphoreTake (semaphore, 100 / portTICK_RATE_MS) == pdTRUE) + { + memcpy (dst, &gpsData, sizeof (gpsData_t)); + xSemaphoreGive (semaphore); + return 1; + } +#endif + + memset (dst, 0, sizeof (gpsData_t)); + return 0; +} + +// +// +// +portTASK_FUNCTION (vGPSTask, pvParameters __attribute__ ((unused))) +{ + int fd; + static char nmeaSentence [GPS_MAX_NMEA_SENTENCE]; + + memset (&gpsData, 0, sizeof (gpsData)); + + vSemaphoreCreateBinary (semaphore); + + fd = open ("/dev/uart1", O_RDONLY); + + if ((fd == -1) || (semaphore == NULL)) + for (;;) + vTaskDelay (100); + + for (;;) + { + portCHAR c; + + if (read (fd, &c, sizeof (c)) == sizeof (c)) + if (gpsProcessByte (c, nmeaSentence)) + gpsDispatchMessages (nmeaSentence); + } +} diff --git a/gps/gps.h b/gps/gps.h new file mode 100644 index 0000000..b8e008f --- /dev/null +++ b/gps/gps.h @@ -0,0 +1,33 @@ +#ifndef _GPS_H_ +#define _GPS_H_ + +#include "semphr.h" + +// +// +// +typedef struct gpsData_s +{ + unsigned char valid; + double latitude; + double longitude; + double height; + float groundSpeed; + float trueCourse; + unsigned char utcDay; + unsigned char utcMonth; + unsigned short utcYear; + unsigned char utcHours; + unsigned char utcMinutes; + unsigned char utcSeconds; + int restarts; +} +gpsData_t; + +// +// +// +int gpsCopyData (gpsData_t *dst); +portTASK_FUNCTION_PROTO (vGPSTask, pvParameters __attribute__ ((unused))); + +#endif diff --git a/i2c/Makefile b/i2c/Makefile new file mode 100644 index 0000000..0aef6ce --- /dev/null +++ b/i2c/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=i2c.c i2cInt.c lm75.c eeprom.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/i2c/eeprom.c b/i2c/eeprom.c new file mode 100644 index 0000000..d537508 --- /dev/null +++ b/i2c/eeprom.c @@ -0,0 +1,149 @@ +#include "FreeRTOS.h" + +#include + +#include "i2c.h" +#include "eeprom.h" + +// +// +// +static U32 rwAddress; +static U8 deviceAddress = EEPROM_ADDRESS; + +// +// +// +void eepromInit (void) +{ +} + +int eepromSetAddress (U32 address) +{ + int r = 0; + + if (address >= EEPROM_SIZE) + r = -1; + + address %= EEPROM_SIZE; + + if ((rwAddress = address) >= 65536) + deviceAddress |= 0x02; + else + deviceAddress &= ~0x02; + + return r; +} + +static int eepromSetAddressEx (U32 address, U8 *buffer) +{ + int r; + + if ((r = eepromSetAddress (address))) + return r; + + buffer [0] = rwAddress >> 8; + buffer [1] = rwAddress; + + return 0; +} + +U32 eepromGetAddress (void) +{ + return rwAddress; +} + +// +// +// +int eepromRead (U8 *buffer, U32 bufferLength) +{ + int r; + + buffer [0] = rwAddress >> 8; + buffer [1] = rwAddress; + + if (!(r = i2cWriteReadBufferPoll (deviceAddress, buffer, sizeof (U16), bufferLength))) + eepromSetAddress (eepromGetAddress () + bufferLength); + + return r; +} + +int eepromReadAddress (U32 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if ((r = eepromSetAddress (address))) + return r; + + return eepromRead (buffer, bufferLength); +} + +// +// +// +int eepromWrite (U8 *buffer, U32 bufferLength) +{ + int r; + + buffer [0] = rwAddress >> 8; + buffer [1] = rwAddress; + + if (!(r = i2cWriteBufferPoll (deviceAddress, buffer, bufferLength + 2))) + eepromSetAddress (eepromGetAddress () + bufferLength); + + return r; +} + +int eepromWriteAddress (U32 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if ((r = eepromSetAddress (address))) + return r; + + return eepromWrite (buffer, bufferLength); +} + +// +// +// +static int eepromFillAddressCommon (U32 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if (!(r = eepromSetAddressEx (address, buffer))) + r = i2cWriteBufferPoll (deviceAddress, buffer, bufferLength + 2); + + return r; +} + +int eepromFillAddress (U32 address, U32 bufferLength, U8 fillValue) +{ + int r; + U32 l; + U32 i; + U8 buffer [EEPROM_PAGESIZE + 2]; + + memset (buffer, fillValue, sizeof (buffer)); + + l = (EEPROM_PAGESIZE - ((address + EEPROM_PAGESIZE) % EEPROM_PAGESIZE)) % EEPROM_PAGESIZE; + l = MIN (l, bufferLength); + + if (l && (r = eepromFillAddressCommon (address, buffer, l))) + return r; + + address += l; + bufferLength -= l; + l = bufferLength - (bufferLength % EEPROM_PAGESIZE); + + for (i = 0; i < l; i += EEPROM_PAGESIZE, address += (sizeof (buffer) - 2), bufferLength -= (sizeof (buffer) - 2)) + if ((r = eepromFillAddressCommon (address, buffer, sizeof (buffer) - 2))) + return r; + + if (bufferLength && (r = eepromFillAddressCommon (address, buffer, bufferLength))) + return r; + + return 0; +} + diff --git a/i2c/eeprom.h b/i2c/eeprom.h new file mode 100644 index 0000000..bfd1412 --- /dev/null +++ b/i2c/eeprom.h @@ -0,0 +1,23 @@ +#ifndef _EEPROM_H_ +#define _EEPROM_H_ + +// +// +// +#define EEPROM_ADDRESS (0xa0) +#define EEPROM_SIZE (131072) +#define EEPROM_PAGESIZE (256) + +// +// +// +void eepromInit (void); +int eepromSetAddress (U32 address); +U32 eepromGetAddress (void); +int eepromRead (U8 *buffer, U32 bufferLength); +int eepromReadAddress (U32 address, U8 *buffer, U32 bufferLength); +int eepromWrite (U8 *buffer, U32 bufferLength); +int eepromWriteAddress (U32 address, U8 *buffer, U32 bufferLength); +int eepromFillAddress (U32 address, U32 length, U8 fillValue); + +#endif diff --git a/i2c/eeprom_working.c b/i2c/eeprom_working.c new file mode 100644 index 0000000..207f0a9 --- /dev/null +++ b/i2c/eeprom_working.c @@ -0,0 +1,156 @@ +#include "FreeRTOS.h" + +#include + +#include "i2c.h" +#include "eeprom.h" + +// +// +// +static U32 rwAddress; +static U8 deviceAddress = EEPROM_ADDRESS; + +// +// +// +void eepromInit (void) +{ +} + +int eepromSetAddress (U32 address) +{ + int r = 0; + + if (address >= EEPROM_SIZE) + r = -1; + + address %= EEPROM_SIZE; + + if ((rwAddress = address) >= 65536) + deviceAddress |= 0x02; + else + deviceAddress &= ~0x02; + + return r; +} + +static int eepromSetAddressEx (U32 address, U8 *buffer) +{ + int r; + + if ((r = eepromSetAddress (address))) + return r; + + buffer [0] = rwAddress >> 8; + buffer [1] = rwAddress; + + return 0; +} + +U32 eepromGetAddress (void) +{ + return rwAddress; +} + +// +// +// +int eepromRead (U8 *buffer, U32 bufferLength) +{ + int r; + + buffer [0] = rwAddress >> 8; + buffer [1] = rwAddress; + + if (!(r = i2cWriteReadBuffer (deviceAddress, buffer, sizeof (U16), bufferLength))) + eepromSetAddress (eepromGetAddress () + bufferLength); + + return r; +} + +int eepromReadAddress (U32 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if ((r = eepromSetAddress (address))) + return r; + + return eepromRead (buffer, bufferLength); +} + +// +// +// +int eepromWrite (U8 *buffer, U32 bufferLength) +{ + int r; + + buffer [0] = rwAddress >> 8; + buffer [1] = rwAddress; + + if (!(r = i2cWriteBuffer (deviceAddress, buffer, bufferLength + 2))) + eepromSetAddress (eepromGetAddress () + bufferLength); + + return r; +} + +int eepromWriteAddress (U32 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if ((r = eepromSetAddress (address))) + return r; + + return eepromWrite (buffer, bufferLength); +} + +// +// +// +int eepromFillAddress (U32 address, U32 bufferLength, U8 fillValue) +{ + int r; + U32 l; + U32 i; + U8 buffer [EEPROM_PAGESIZE + 2]; + + memset (buffer, fillValue, sizeof (buffer)); + + l = (EEPROM_PAGESIZE - ((address + EEPROM_PAGESIZE) % EEPROM_PAGESIZE)) % EEPROM_PAGESIZE; + l = MIN (l, bufferLength); + + if (l) + { + if ((r = eepromSetAddressEx (address, buffer))) + return r; + + if ((r = i2cWriteBuffer (deviceAddress, buffer, l + 2))) + return r; + } + + address += l; + bufferLength -= l; + l = bufferLength - (bufferLength % EEPROM_PAGESIZE); + + for (i = 0; i < l; i += EEPROM_PAGESIZE, address += (sizeof (buffer) - 2), bufferLength -= (sizeof (buffer) - 2)) + { + if ((r = eepromSetAddressEx (address, buffer))) + return r; + + if ((r = i2cWriteBuffer (deviceAddress, buffer, sizeof (buffer)))) + return r; + } + + if (bufferLength) + { + if ((r = eepromSetAddressEx (address, buffer))) + return r; + + if ((r = i2cWriteBuffer (deviceAddress, buffer, bufferLength + 2))) + return r; + } + + return 0; +} + diff --git a/i2c/i2c.c b/i2c/i2c.c new file mode 100644 index 0000000..e2732bb --- /dev/null +++ b/i2c/i2c.c @@ -0,0 +1,60 @@ +#include "FreeRTOS.h" + +#include "i2c.h" + +// +// +// +typedef struct i2cErrnoStr_s +{ + int errno; + const char *text; +} +i2cErrnoStr_t; + +static i2cErrnoStr_t i2cErrnoStr [] = +{ + { I2CERR_NONE, "I2CERR_NONE" }, + { I2CERR_BUSY, "I2CERR_BUSY" }, + { I2CERR_EMPTY, "I2CERR_EMPTY" }, + { I2CERR_TIMEOUT, "I2CERR_TIMEOUT" }, + { I2CERR_TIMEOUTWC, "I2CERR_TIMEOUTWC" }, + { I2CERR_TIMEOUTACKPOLL, "I2CERR_TIMEOUTACKPOLL" }, + { I2CERR_NOTIMPLEMENTED, "I2CERR_NOTIMPLEMENTED" }, + { I2CERR_OTHER, "I2CERR_OTHER" }, + { I2CERR_BUSERROR, "I2CERR_BUSERROR" }, + { I2CERR_BUSERRORx, "I2CERR_BUSERRORx" }, + { I2CERR_STARTTX, "I2CERR_STARTTX" }, + { I2CERR_REPEATEDSTARTTX, "I2CERR_REPEATEDSTARTTX" }, + { I2CERR_SLAWTX_ACKRX, "I2CERR_SLAWTX_ACKRX" }, + { I2CERR_SLAWTX_NACKRX, "I2CERR_SLAWTX_NACKRX" }, + { I2CERR_DATTX_ACKRX, "I2CERR_DATTX_ACKRX" }, + { I2CERR_DATTX_NACKRX, "I2CERR_DATTX_NACKRX" }, + { I2CERR_ARBLOST, "I2CERR_ARBLOST" }, + { I2CERR_SLARTX_ACKRX, "I2CERR_SLARTX_ACKRX" }, + { I2CERR_SLARTX_NACKRX, "I2CERR_SLARTX_NACKRX" }, + { I2CERR_DATRX_ACKTX, "I2CERR_DATRX_ACKTX" }, + { I2CERR_DATRX_NACKTX, "I2CERR_DATRX_NACKTX" }, + { I2CERR_NOINFO, "I2CERR_NOINFO" }, +}; + +i2cErr_e i2cErrno = I2CERR_NONE; + +// +// +// +int i2cGetErrno (void) +{ + return i2cErrno; +} + +const char *i2cStrerror (int err) +{ + unsigned int i; + + for (i = 0; i < arrsizeof (i2cErrnoStr); i++) + if (i2cErrnoStr [i].errno == err) + return i2cErrnoStr [i].text; + + return NULL; +} diff --git a/i2c/i2c.h b/i2c/i2c.h new file mode 100644 index 0000000..cfaa16c --- /dev/null +++ b/i2c/i2c.h @@ -0,0 +1,58 @@ +#ifndef _I2C_H_ +#define _I2C_H_ + +#include "sysdefs.h" + +// +// Does not define slave RX statuses. Bus errors (I2C_STAT == 0) is remapped +// to I2CERR_ERROR in i2cStatus() so that we can use a value of 0 to indicate +// no error. +// +typedef enum +{ + I2CERR_NONE = 0, + I2CERR_BUSY, + I2CERR_EMPTY, + I2CERR_TIMEOUT, + I2CERR_TIMEOUTWC, + I2CERR_TIMEOUTACKPOLL, + I2CERR_NOTIMPLEMENTED, + I2CERR_OTHER, + I2CERR_BUSERROR, + I2CERR_BUSERRORx = 0x00, + I2CERR_STARTTX = 0x08, + I2CERR_REPEATEDSTARTTX = 0x10, + I2CERR_SLAWTX_ACKRX = 0x18, + I2CERR_SLAWTX_NACKRX = 0x20, + I2CERR_DATTX_ACKRX = 0x28, + I2CERR_DATTX_NACKRX = 0x30, + I2CERR_ARBLOST = 0x38, + I2CERR_SLARTX_ACKRX = 0x40, + I2CERR_SLARTX_NACKRX = 0x48, + I2CERR_DATRX_ACKTX = 0x50, + I2CERR_DATRX_NACKTX = 0x58, + I2CERR_NOINFO = 0xf8 +} +i2cErr_e; + +// +// +// +extern i2cErr_e i2cErrno; + +// +// +// +void i2cInit (void); +int i2cGetErrno (void); +const char *i2cStrerror (int err); +void i2cSetTimeout (unsigned int timeoutInMilliseconds); +void i2cDump (void); +int i2cWriteBuffer (U8 address, U8 *buffer, U32 bufferLength); +int i2cReadBuffer (U8 address, U8 *buffer, U32 bufferLength); +int i2cWriteReadBuffer (U8 address, U8 *buffer, U32 putLength, U32 getLength); +int i2cWriteBufferPoll (U8 address, U8 *buffer, U32 bufferLength); +int i2cWriteReadBufferPoll (U8 address, U8 *buffer, U32 putLength, U32 getLength); + + +#endif diff --git a/i2c/i2cInt.c b/i2c/i2cInt.c new file mode 100644 index 0000000..80f0bdf --- /dev/null +++ b/i2c/i2cInt.c @@ -0,0 +1,405 @@ +#include "FreeRTOS.h" + +#include +#include +#include + +#include "i2c.h" + +// +// Default timeout, in milliseconds for generic read/write +// +#define I2C_DEFAULT_TIMEOUT 100 +#undef I2C_DEBUG + +// +// +// +typedef enum +{ + ACKPOLL_NO = 0, + ACKPOLL_YES +} +ackPoll_e; + +// +// +// +static volatile int i2cBusInUse; +static U8 i2cAddress; +static U8 *i2cDataBuffer; +static int i2cDataLenWrite; +static int i2cDataLenRead; +static unsigned int i2cTimeoutInTicks = I2C_DEFAULT_TIMEOUT / portTICK_RATE_MS; +static int i2cDataCnt; +static ackPoll_e i2cAckPoll = ACKPOLL_NO; + +// +// +// +#ifdef I2C_DEBUG +static U8 i2cTransactions [64]; +static int i2cTransactionsIndex; +#define i2cDebug(x) do { if (i2cAckPoll && (i2cTransactionsIndex < (int) sizeof (i2cTransactions))) i2cTransactions [i2cTransactionsIndex++] = x; } while (0) +#define i2cDebugReset() do { i2cTransactionsIndex = 0; } while (0) +#else +#define i2cDebug(x) do { } while (0) +#define i2cDebugReset() do { } while (0) +#endif + +static void i2cISR (void) __attribute__ ((interrupt ("IRQ"))); +static void i2cISR (void) +{ + i2cErrno = (I2C0_STAT & I2C_STAT_STATMASK); + i2cDebug (i2cErrno); + + switch (i2cErrno) + { + // + // Transmit conditions + // + case I2CERR_BUSERRORx : // 0x00 + { + I2C0_CONSET = I2C_CONSET_STO | I2C_CONSET_AA; + i2cAckPoll = ACKPOLL_NO; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_STARTTX : // 0x08 + case I2CERR_REPEATEDSTARTTX : // 0x10 + { + i2cDebug (i2cAddress); + I2C0_DAT = i2cAddress; + } + break; + + case I2CERR_SLAWTX_ACKRX : // 0x18 + case I2CERR_DATTX_ACKRX : // 0x28 + { + i2cAckPoll = ACKPOLL_NO; + + if (i2cDataLenWrite && (i2cDataCnt < i2cDataLenWrite)) + { + i2cDebug (i2cDataBuffer [i2cDataCnt]); + I2C0_DAT = i2cDataBuffer [i2cDataCnt++]; + I2C0_CONCLR = I2C_CONCLR_STAC; + } + else + { + if (!i2cDataLenRead) + { + i2cDebug (0xff); + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_STO; + i2cErrno = I2CERR_NONE; + i2cBusInUse = FALSE; + } + else + { + i2cDebug (0xfe); + i2cAddress |= 0x01; + i2cDataCnt = 0; + I2C0_CONSET = I2C_CONSET_STA; + } + } + } + break; + + case I2CERR_DATRX_NACKTX : // 0x58 + { + i2cDataBuffer [i2cDataCnt++] = I2C0_DAT; + i2cDebug (i2cDataBuffer [i2cDataCnt - 1]); + i2cErrno = I2CERR_NONE; + } + + case I2CERR_SLAWTX_NACKRX : // 0x20 + case I2CERR_SLARTX_NACKRX : // 0x48 + { + if (i2cAckPoll) + break; + } + + case I2CERR_DATTX_NACKRX : // 0x30 + { + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_STO; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_ARBLOST : // 0x38 + { + I2C0_CONSET = I2C_CONSET_STA; + } + break; + + // + // Receive byte conditions (fall through is intentional) + // + case I2CERR_DATRX_ACKTX : // 0x50 + { + i2cDataBuffer [i2cDataCnt++] = I2C0_DAT; + i2cDebug (i2cDataBuffer [i2cDataCnt - 1]); + } + + case I2CERR_SLARTX_ACKRX : // 0x40 + { + if (i2cDataCnt < i2cDataLenRead - 1) + { + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_AA; + } + else + I2C0_CONCLR = I2C_CONCLR_STAC | I2C_CONCLR_AAC; + } + break; + + case I2CERR_NOINFO : + break; + + default: + { + I2C0_CONCLR = I2C_CONCLR_I2ENC; + i2cAckPoll = ACKPOLL_NO; + i2cBusInUse = FALSE; + } + break; + } + + I2C0_CONCLR = I2C_CONSET_SI; + VIC_VectAddr = 0; +} + +// +// i2c1Init +// +void i2cInit (void) +{ + i2cBusInUse = FALSE; + i2cAckPoll = ACKPOLL_NO; + + SCB_PCONP |= SCB_PCONP_PCI2C0; + + PCB_PINSEL0 = (PCB_PINSEL0 & ~(PCB_PINSEL0_P02_MASK | PCB_PINSEL0_P03_MASK)) | (PCB_PINSEL0_P02_SCL0 | PCB_PINSEL0_P03_SDA0); + + I2C0_CONCLR = I2C_CONCLR_MASK; + I2C0_CONSET = I2C_CONSET_I2EN; + I2C0_SCLL = 240; + I2C0_SCLH = 240; + + // + // Initialize the interrupt vector + // + VIC_IntSelect &= ~VIC_IntSelect_I2C0; + VIC_VectCntl7 = VIC_VectCntl_ENABLE | VIC_Channel_I2C0; + VIC_VectAddr7 = (int) i2cISR; + VIC_IntEnable = VIC_IntEnable_I2C0; +} + +// +// +// +static int i2cWaitComplete (int ticks) +{ + if (i2cBusInUse) + { + clock_t theFuture; + + for (theFuture = times (NULL) + ticks; i2cBusInUse; ) + { + if (times (NULL) > theFuture) + { + I2C0_CONCLR = I2C_CONCLR_I2ENC; + i2cErrno = I2CERR_TIMEOUTWC; + return -1; + } + } + } + + return (i2cErrno == I2CERR_NONE) ? 0 : -1; +} + +static int i2cPoll (U8 address) +{ + clock_t theFuture = times (NULL) + 10; + + VIC_IntEnClr = VIC_IntEnClr_I2C0; + + while (times (NULL) < theFuture) + { + I2C0_CONCLR = I2C_CONSET_SI; + I2C0_CONSET = I2C_CONSET_STA; + + while (!(I2C0_CONSET & I2C_CONSET_SI)) + ; + + I2C0_CONCLR = I2C_CONCLR_STAC | I2C_CONCLR_SIC; + I2C0_DAT = address & ~0x01; + + while (!(I2C0_CONSET & I2C_CONSET_SI)) + ; + + if ((i2cErrno = I2C0_STAT) == I2CERR_SLAWTX_ACKRX) + break; + } + + if (i2cErrno != I2CERR_SLAWTX_ACKRX) + i2cErrno = I2CERR_TIMEOUTACKPOLL; + + I2C0_CONCLR = I2C_CONCLR_SIC; + I2C0_CONSET = I2C_CONSET_STO | I2C_CONSET_AA; + + while (I2C0_CONSET & I2C_CONSET_STO) + ; + + VIC_IntEnable = VIC_IntEnable_I2C0; + + return (i2cErrno == I2CERR_SLAWTX_ACKRX) ? 0 : -1; +} + +// +// +// +static int i2cKickOff (U8 address, U8 *buffer, int bufferLenWrite, int bufferLenRead, ackPoll_e ackPoll) +{ + // + // Determine if our first operation will be a write or read. If both, the + // write always occurs first. + // + if (bufferLenWrite) + address &= ~0x01; + else if (bufferLenRead) + address |= 0x01; + else + address &= ~0x01; + + // + // Wait until last I2C operation has finished. + // + if (i2cBusInUse && i2cWaitComplete (i2cTimeoutInTicks)) + { + i2cErrno = I2CERR_TIMEOUT; + return -1; + } + + // + // Mark bus as in use, save the address, buffer and length + // + i2cBusInUse = TRUE; + i2cAddress = address; + i2cDataBuffer = buffer; + i2cDataLenWrite = bufferLenWrite; + i2cDataLenRead = bufferLenRead; + i2cDataCnt = 0; + i2cAckPoll = ackPoll; + i2cDebugReset (); + + // + // Clear any outstanding bits, enable the interface, and send a start bit. + // Once the start bit goes out, the interrupt drive state machine begins. + // + I2C0_CONCLR = I2C_CONCLR_MASK; + I2C0_CONSET = I2C_CONSET_I2EN; + I2C0_CONSET = I2C_CONSET_STA; + + return 0; +} + +// +// +// +static int i2cWriteBufferEx (U8 address, U8 *buffer, U32 bufferLength, int milliseconds) +{ + int r; + + if (!(r = i2cKickOff (address, buffer, bufferLength, 0, ACKPOLL_NO))) + r = i2cWaitComplete (milliseconds / portTICK_RATE_MS); + + return r; +} + +static int i2cReadBufferEx (U8 address, U8 *buffer, U32 bufferLength, int milliseconds) +{ + int r; + + if (!(r = i2cKickOff (address, buffer, 0, bufferLength, ACKPOLL_NO))) + r = i2cWaitComplete (milliseconds / portTICK_RATE_MS); + + return r; +} + +static int i2cWriteReadBufferEx (U8 address, U8 *buffer, U32 putLength, U32 getLength, int milliseconds) +{ + int r; + + if (!(r = i2cKickOff (address, buffer, putLength, getLength, ACKPOLL_NO))) + r = i2cWaitComplete (milliseconds / portTICK_RATE_MS); + + return r; +} + +// +// +// +void i2cSetTimeout (unsigned int timeoutInMilliseconds) +{ + if (timeoutInMilliseconds < portTICK_RATE_MS) + timeoutInMilliseconds = portTICK_RATE_MS; + + i2cTimeoutInTicks = timeoutInMilliseconds / portTICK_RATE_MS; +} + +void i2cDump (void) +{ +#ifdef I2C_DEBUG + int i; + + for (i = 0; i < i2cTransactionsIndex; i++) + printf ("0x%02x ", i2cTransactions [i]); + + printf ("\n"); +#else + printf ("Not compiled for i2c debugging\n"); +#endif +} + +// +// DANGER, WILL ROBINSON! The callers buffer must persist until we're done +// +int i2cWriteBuffer (U8 address, U8 *buffer, U32 bufferLength) +{ + return i2cWriteBufferEx (address, buffer, bufferLength, i2cTimeoutInTicks); +} + +int i2cReadBuffer (U8 address, U8 *buffer, U32 bufferLength) +{ + return i2cReadBufferEx (address, buffer, bufferLength, i2cTimeoutInTicks); +} + +int i2cWriteReadBuffer (U8 address, U8 *buffer, U32 putLength, U32 getLength) +{ + return i2cWriteReadBufferEx (address, buffer, putLength, getLength, i2cTimeoutInTicks); +} + +int i2cWriteBufferPoll (U8 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if (!(r = i2cKickOff (address, buffer, bufferLength, 0, ACKPOLL_NO))) + if (!(r = i2cWaitComplete (i2cTimeoutInTicks))) + r = i2cPoll (address); + + return r; +} + +int i2cWriteReadBufferPoll (U8 address, U8 *buffer, U32 putLength, U32 getLength) +{ + int r; + + if (!(r = i2cKickOff (address, buffer, putLength, getLength, ACKPOLL_NO))) + if (!(r = i2cWaitComplete (i2cTimeoutInTicks))) + r = i2cPoll (address); + + return r; +} diff --git a/i2c/i2cIntWorking.c b/i2c/i2cIntWorking.c new file mode 100644 index 0000000..7ef4f11 --- /dev/null +++ b/i2c/i2cIntWorking.c @@ -0,0 +1,342 @@ +#include "FreeRTOS.h" + +#include +#include +#include + +#include "i2c.h" + +// +// Default timeout, in milliseconds for generic read/write +// +#define I2C_DEFAULT_TIMEOUT 100 + +// +// +// +static volatile int i2cBusInUse; +static U8 i2cAddress; +static U8 *i2cDataBuffer; +static int i2cDataLenWrite; +static int i2cDataLenRead; +static int i2cTimeout = I2C_DEFAULT_TIMEOUT; +static U8 *i2cDataPtr; +static U8 i2cTransactions [64]; +static int i2cTransactionsIndex; + +// +// +// +#define i2cDebug(x) do { if (i2cTransactionsIndex < (int) sizeof (i2cTransactions)) i2cTransactions [i2cTransactionsIndex++] = x; } while (0) + +static void i2cISR (void) __attribute__ ((interrupt ("IRQ"))); +static void i2cISR (void) +{ + i2cErrno = (I2C0_STAT & I2C_STAT_STATMASK); + i2cDebug (i2cErrno); + + switch (i2cErrno) + { + // + // Transmit conditions + // + case I2CERR_BUSERRORx : // 0x00 + { + I2C0_CONSET = I2C_CONSET_STO | I2C_CONSET_AA; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_STARTTX : // 0x08 + { + i2cDebug (i2cAddress); + I2C0_DAT = i2cAddress; + } + break; + + case I2CERR_REPEATEDSTARTTX : // 0x10 + { + i2cDebug (i2cAddress); + I2C0_DAT = i2cAddress; + } + break; + + case I2CERR_SLAWTX_ACKRX : // 0x18 + { + i2cDebug (*i2cDataPtr); + I2C0_DAT = *i2cDataPtr++; + I2C0_CONCLR = I2C_CONCLR_STAC; + } + break; + + case I2CERR_SLAWTX_NACKRX : // 0x20 + { + I2C0_CONSET = I2C_CONSET_STO; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_DATTX_ACKRX : // 0x28 + { + if (--i2cDataLenWrite) + { + i2cDebug (*i2cDataPtr); + I2C0_DAT = *i2cDataPtr++; + I2C0_CONCLR = I2C_CONCLR_STAC; + } + else + { + if (!i2cDataLenRead) + { + i2cDebug (0xff); + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_STO; + i2cBusInUse = FALSE; + } + else + { + i2cDebug (0xfe); + i2cAddress |= 0x01; + i2cDataPtr = i2cDataBuffer; + I2C0_CONSET = I2C_CONSET_STA; + } + } + } + break; + + case I2CERR_DATTX_NACKRX : // 0x30 + { + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_STO; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_ARBLOST : // 0x38 + { + I2C0_CONSET = I2C_CONSET_STA; + } + break; + + // + // Receive byte conditions + // + case I2CERR_SLARTX_ACKRX : // 0x40 + { + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_AA; + } + break; + + case I2CERR_SLARTX_NACKRX : // 0x48 + { + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_STO; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_DATRX_ACKTX : // 0x50 + { + *i2cDataPtr++ = I2C0_DAT; + i2cDebug (*(i2cDataPtr - 1)); + + if (--i2cDataLenRead) + { + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_AA; + } + else + { + I2C0_CONCLR = I2C_CONCLR_STAC | I2C_CONCLR_AAC; + i2cBusInUse = FALSE; + } + } + break; + + case I2CERR_DATRX_NACKTX : // 0x58 + { + // *i2cDataPtr = I2C0_DAT; + // i2cDebug (*i2cDataPtr); + I2C0_CONCLR = I2C_CONCLR_STAC; + I2C0_CONSET = I2C_CONSET_STO; + i2cBusInUse = FALSE; + } + break; + + case I2CERR_NOINFO : + break; + + default: + { + I2C0_CONCLR = I2C_CONCLR_I2ENC; + i2cBusInUse = FALSE; + } + break; + } + + I2C0_CONCLR = I2C_CONSET_SI; + VIC_VectAddr = 0; +} + +// +// i2c1Init +// +void i2cInit (void) +{ + i2cBusInUse = FALSE; + + SCB_PCONP |= SCB_PCONP_PCI2C0; + + PCB_PINSEL0 = (PCB_PINSEL0 & ~(PCB_PINSEL0_P02_MASK | PCB_PINSEL0_P03_MASK)) | (PCB_PINSEL0_P02_SCL0 | PCB_PINSEL0_P03_SDA0); + + I2C0_CONCLR = I2C_CONCLR_MASK; + I2C0_CONSET = I2C_CONSET_I2EN; + I2C0_SCLL = 240; + I2C0_SCLH = 240; + + // + // Initialize the interrupt vector + // + VIC_IntSelect &= ~VIC_IntSelect_I2C0; + VIC_VectCntl7 = VIC_VectCntl_ENABLE | VIC_Channel_I2C0; + VIC_VectAddr7 = (int) i2cISR; + VIC_IntEnable = VIC_IntEnable_I2C0; +} + +// +// +// +static int i2cTransferBytes (U8 address, U8 *buffer, int bufferLenWrite, int bufferLenRead) +{ + // + // Determine if our first operation will be a write or read. If both, the + // write always occurs first. + // + if (bufferLenWrite) + address &= ~0x01; + else if (bufferLenRead) + address |= 0x01; + else + { + i2cErrno = I2CERR_OTHER; + return -1; + } + + // + // Wait until last I2C operation has finished. + // + if (i2cBusInUse && i2cWaitComplete (i2cTimeout)) + { + i2cErrno = I2CERR_TIMEOUT; + return -1; + } + + // + // Mark bus as in use, save the address, buffer and length + // + i2cBusInUse = TRUE; + i2cAddress = address; + i2cDataBuffer = buffer; + i2cDataLenWrite = bufferLenWrite; + i2cDataLenRead = bufferLenRead; + i2cDataPtr = buffer; + + i2cTransactionsIndex = 0; // ### + + I2C0_CONCLR = I2C_CONCLR_MASK; + I2C0_CONSET = I2C_CONSET_I2EN; + I2C0_CONSET = I2C_CONSET_STA; + + return 0; +} + +// +// +// +int i2cWaitComplete (int milliseconds) +{ + if (i2cBusInUse) + { + clock_t theFuture; + + if (milliseconds < 10) + milliseconds = 10; + + for (theFuture = times (NULL) + (milliseconds / 10); i2cBusInUse; ) + { + if (times (NULL) > theFuture) + { + I2C0_CONCLR = I2C_CONCLR_I2ENC; + i2cErrno = I2CERR_TIMEOUTWC; + return -1; + } + } + } + + return 0; +} + +// +// +// +static int i2cWriteBufferEx (U8 address, U8 *buffer, U32 bufferLength, int milliseconds) +{ + if (i2cTransferBytes (address, buffer, bufferLength, 0)) + return -1; + + return i2cWaitComplete (milliseconds); +} + +static int i2cReadBufferEx (U8 address, U8 *buffer, U32 bufferLength, int milliseconds) +{ + if (i2cTransferBytes (address, buffer, 0, bufferLength)) + return -1; + + return i2cWaitComplete (milliseconds); +} + +static int i2cWriteReadBufferEx (U8 address, U8 *buffer, U32 putLength, U32 getLength, int milliseconds) +{ + if (i2cTransferBytes (address, buffer, putLength, getLength)) + return -1; + + return i2cWaitComplete (milliseconds); +} + +// +// DANGER, WILL ROBINSON! The callers buffer must persist until we're done +// +int i2cWriteBuffer (U8 address, U8 *buffer, U32 bufferLength) +{ + return i2cWriteBufferEx (address, buffer, bufferLength, i2cTimeout); +} + +int i2cReadBuffer (U8 address, U8 *buffer, U32 bufferLength) +{ + return i2cReadBufferEx (address, buffer, bufferLength, i2cTimeout); +} + +int i2cWriteReadBuffer (U8 address, U8 *buffer, U32 putLength, U32 getLength) +{ + return i2cWriteReadBufferEx (address, buffer, putLength, getLength, i2cTimeout); +} + +void i2cSetTimeout (int timeoutInMilliseconds) +{ + if (timeoutInMilliseconds < 10) + timeoutInMilliseconds = 10; + + i2cTimeout = timeoutInMilliseconds; +} + +void i2cDump (void) +{ + int i; + + for (i = 0; i < i2cTransactionsIndex; i++) + printf ("0x%02x ", i2cTransactions [i]); + + printf ("\n"); +} + diff --git a/i2c/i2cPolled.c b/i2c/i2cPolled.c new file mode 100644 index 0000000..3b0aa80 --- /dev/null +++ b/i2c/i2cPolled.c @@ -0,0 +1,412 @@ +#include "FreeRTOS.h" + +#include +#include + +#include "i2c.h" + +// +// Default timeout, in milliseconds for generic read/write +// +#define I2C_DEFAULT_TIMEOUT 100 + +// +// +// +typedef enum +{ + I2CFLAGS_START = 0x0001, + I2CFLAGS_REPEATEDSTART = 0x0002, + I2CFLAGS_STOP = 0x0004, + I2CFLAGS_ADDRESS = 0x0008, + I2CFLAGS_WRITEDATA = 0x0010, + I2CFLAGS_READDATA = 0x0020, +} +i2cFlags_e; + +typedef enum +{ + I2CMODE_ACK = 0, + I2CMODE_NACK, + I2CMODE_READ +} +i2cMode_e; + +// +// +// +static unsigned int i2cTimeoutInTicks = I2C_DEFAULT_TIMEOUT / portTICK_RATE_MS; + +// +// Our PCLK is 48Mhz (12Mhz Xtal, PLL x 4, VBPDIV = /1), we want 100Khz SCLK +// +void i2cInit (void) +{ + SCB_PCONP |= SCB_PCONP_PCI2C0; + + PCB_PINSEL0 = (PCB_PINSEL0 & ~(PCB_PINSEL0_P02_MASK | PCB_PINSEL0_P03_MASK)) | (PCB_PINSEL0_P02_SCL0 | PCB_PINSEL0_P03_SDA0); + + I2C0_CONCLR = I2C_CONCLR_MASK; + I2C0_CONSET = I2C_CONSET_I2EN; + I2C0_SCLL = 480; + I2C0_SCLH = 480; +} + +// +// +// +static i2cErr_e i2cStatus (void) +{ + i2cErr_e status; + + while (!(I2C0_CONSET & I2C_CONSET_SI)) + ; + + if ((status = I2C0_STAT) == I2CERR_BUSERRORx) + return I2CERR_BUSERROR; + + return status; +} + +// +// +// +static i2cErr_e i2cStop (void) +{ + I2C0_CONCLR = I2C_CONCLR_SIC; + I2C0_CONSET = I2C_CONSET_STO; + + while (I2C0_CONSET & I2C_CONSET_STO) + ; + + return I2CERR_NONE; +} + +// +// +// +static i2cErr_e i2cStart (void) +{ + I2C0_CONCLR = I2C_CONCLR_SIC; + I2C0_CONSET = I2C_CONSET_STA; + + while (1) + { + i2cErr_e status; + + if (((status = i2cStatus ()) == I2CERR_STARTTX) || (status == I2CERR_REPEATEDSTARTTX)) + { + I2C0_CONCLR = I2C_CONCLR_STAC; + return I2CERR_NONE; + } + else if (status != I2CERR_NOINFO) + { + I2C0_CONCLR = I2C_CONCLR_STAC; + return status; + } + else + I2C0_CONCLR = I2C_CONCLR_SIC; + } +} + +// +// +// +static i2cErr_e i2cRepeatedStart (void) +{ + while (!(I2C0_CONSET & I2C_CONSET_SI)) + ; + + I2C0_CONCLR = I2C_CONCLR_SIC; + I2C0_CONSET = I2C_CONSET_STA; + + while (1) + { + i2cErr_e status; + + if (((status = i2cStatus ()) == I2CERR_STARTTX) || (status == I2CERR_REPEATEDSTARTTX)) + { + I2C0_CONCLR = I2C_CONCLR_STAC; + return I2CERR_NONE; + } + else if (status != I2CERR_NOINFO) + { + I2C0_CONCLR = I2C_CONCLR_STAC; + return status; + } + else + I2C0_CONCLR = I2C_CONCLR_SIC; + } +} + +// +// +// +static i2cErr_e i2cPutByte (U8 data) +{ + if (!(I2C0_CONSET & I2C_CONSET_SI)) + return I2CERR_BUSY; + + I2C0_DAT = data; + I2C0_CONCLR = I2C_CONCLR_SIC; + + return I2CERR_NONE; +} + +// +// +// +static i2cErr_e i2cGetByte (i2cMode_e mode, U8 *pData) +{ + switch (mode) + { + case I2CMODE_ACK : + I2C0_CONCLR = I2C_CONCLR_SIC; + I2C0_CONSET = I2C_CONSET_AA; + break; + + case I2CMODE_NACK : + I2C0_CONCLR = (I2C_CONCLR_AAC | I2C_CONCLR_SIC); + break; + + case I2CMODE_READ : + { + if (!(I2C0_CONSET & I2C_CONSET_SI)) + return I2CERR_EMPTY; + + *pData = (U8) I2C0_DAT; + } + break; + } + + return I2CERR_NONE; +} + +// +// +// +static i2cErr_e i2cWriteBufferEx (U8 address, U8 *buffer, U32 bufferLength, i2cFlags_e flags) +{ + U32 i; + i2cErr_e status; + + if (flags & I2CFLAGS_START) + { + if ((status = i2cStart ()) != I2CERR_NONE) + { + i2cStop (); + return status; + } + } + else if (flags & I2CFLAGS_REPEATEDSTART) + { + if ((status = i2cRepeatedStart ()) != I2CERR_NONE) + { + i2cStop (); + return status; + } + } + + if (flags & I2CFLAGS_ADDRESS) + { + do + if (((status = i2cPutByte (address & ~0x01)) != I2CERR_NONE) && (status != I2CERR_BUSY)) + return status; + while (status == I2CERR_BUSY); + } + + if (flags & I2CFLAGS_WRITEDATA) + { + for (i = 0; i < bufferLength; i++, buffer++) + { + while (1) + { + if (((status = i2cStatus ()) == I2CERR_SLAWTX_ACKRX) || (status == I2CERR_DATTX_ACKRX)) + { + do + if (((status = i2cPutByte (*buffer)) != I2CERR_NONE) && (status != I2CERR_BUSY)) + return status; + while (status == I2CERR_BUSY); + + break; + } + else if (status != I2CERR_NOINFO) + { + i2cStop (); + return status; + } + } + } + } + + if (flags & I2CFLAGS_STOP) + { + while (1) + { + if (((status = i2cStatus ()) == I2CERR_SLAWTX_ACKRX) || (status == I2CERR_DATTX_ACKRX)) + { + i2cStop (); + return I2CERR_NONE; + } + else if (status != I2CERR_NOINFO) + { + i2cStop (); + return status; + } + } + } + + return I2CERR_NONE; +} + +// +// +// +static i2cErr_e i2cReadBufferEx (U8 address, U8 *buffer, U32 bufferLength, i2cFlags_e flags) +{ + U32 i; + i2cErr_e status; + + if (flags & I2CFLAGS_START) + { + if ((status = i2cStart ()) != I2CERR_NONE) + { + i2cStop (); + return status; + } + } + else if (flags & I2CFLAGS_REPEATEDSTART) + { + if ((status = i2cRepeatedStart ()) != I2CERR_NONE) + { + i2cStop (); + return status; + } + } + + if (flags & I2CFLAGS_ADDRESS) + { + do + if (((status = i2cPutByte (address | 0x01)) != I2CERR_NONE) && (status != I2CERR_BUSY)) + return status; + while (status == I2CERR_BUSY); + } + + if (flags & I2CFLAGS_READDATA) + { + for (i = 0; i < bufferLength; i++, buffer++) + { + while (1) + { + if (((status = i2cStatus ()) == I2CERR_SLARTX_ACKRX) || (status == I2CERR_SLARTX_NACKRX) || (status == I2CERR_DATRX_ACKTX)) + { + i2cGetByte ((i != bufferLength - 1) ? I2CMODE_ACK : I2CMODE_NACK, NULL); + + do + status = i2cGetByte (I2CMODE_READ, buffer); + while (status == I2CERR_EMPTY); + + break; + } + else if (status != I2CERR_NOINFO) + { + i2cStop (); + return status; + } + } + } + } + + if (flags & I2CFLAGS_STOP) + i2cStop (); + + return I2CERR_NONE; +} + +// +// +// +static int i2cPoll (U8 address) +{ + clock_t theFuture = times (NULL) + i2cTimeoutInTicks; + + while (times (NULL) < theFuture) + { + if ((i2cErrno = i2cStart ()) != I2CERR_NONE) + break; + if ((i2cErrno = i2cPutByte (address & ~0x01)) != I2CERR_NONE) + break; + if ((i2cErrno = i2cStatus ()) == I2CERR_SLAWTX_ACKRX) + break; + } + + if (i2cErrno != I2CERR_SLAWTX_ACKRX) + i2cErrno = I2CERR_TIMEOUTACKPOLL; + + i2cStop (); + + return (i2cErrno == I2CERR_SLAWTX_ACKRX) ? 0 : -1; +} + +// +// +// +void i2cSetTimeout (unsigned int timeoutInMilliseconds) +{ + if (timeoutInMilliseconds < portTICK_RATE_MS) + timeoutInMilliseconds = portTICK_RATE_MS; + + i2cTimeoutInTicks = timeoutInMilliseconds / portTICK_RATE_MS; +} + +void i2cDump (void) +{ + printf ("i2cDump not implemented for polled mode\n"); +} + +int i2cWriteBuffer (U8 address, U8 *buffer, U32 bufferLength) +{ + if ((i2cErrno = i2cWriteBufferEx (address, buffer, bufferLength, I2CFLAGS_START | I2CFLAGS_ADDRESS | I2CFLAGS_WRITEDATA | I2CFLAGS_STOP)) != I2CERR_NONE) + return -1; + + return 0; +} + +int i2cReadBuffer (U8 address, U8 *buffer, U32 bufferLength) +{ + if ((i2cErrno = i2cReadBufferEx (address, buffer, bufferLength, I2CFLAGS_START | I2CFLAGS_ADDRESS | I2CFLAGS_READDATA | I2CFLAGS_STOP)) != I2CERR_NONE) + return -1; + + return 0; +} + +int i2cWriteReadBuffer (U8 address, U8 *buffer, U32 putLength, U32 getLength) +{ + if ((i2cErrno = i2cWriteBufferEx (address, buffer, putLength, I2CFLAGS_START | I2CFLAGS_ADDRESS | I2CFLAGS_WRITEDATA)) != I2CERR_NONE) + return -1; + + if ((i2cErrno = i2cReadBufferEx (address, buffer, getLength, I2CFLAGS_REPEATEDSTART | I2CFLAGS_ADDRESS | I2CFLAGS_READDATA | I2CFLAGS_STOP)) != I2CERR_NONE) + return -1; + + return 0; +} + +int i2cWriteBufferPoll (U8 address, U8 *buffer, U32 bufferLength) +{ + int r; + + if (!(r = i2cWriteBuffer (address, buffer, bufferLength))) + r = i2cPoll (address); + + return r; +} + +int i2cWriteReadBufferPoll (U8 address, U8 *buffer, U32 putLength, U32 getLength) +{ + int r; + + if (!(r = i2cWriteReadBuffer (address, buffer, putLength, getLength))) + r = i2cPoll (address); + + return r; +} diff --git a/i2c/lm75.c b/i2c/lm75.c new file mode 100644 index 0000000..a334f54 --- /dev/null +++ b/i2c/lm75.c @@ -0,0 +1,158 @@ +#include "FreeRTOS.h" + +#include "i2c.h" +#include "lm75.h" + +// +// +// +static U8 lm75Address = LM75_ADDRESS; +static U8 lm75LastRegister = LM75_REGISTER_TEMPERATURE; +static U8 lm75Mode = 0; + +// +// +// +static int lm75Read8 (U8 reg, int *value) +{ + lm75LastRegister = reg; + *value = 0; + + if (i2cWriteBuffer (lm75Address, (U8 *) ®, sizeof (U8))) + return -1; + + return i2cReadBuffer (lm75Address, (U8 *) value, sizeof (U8)); +} + +static int lm75Write8 (U8 reg, int value) +{ + U8 buffer [2]; + + buffer [0] = lm75LastRegister = reg; + buffer [1] = value; + + return i2cWriteBuffer (lm75Address, buffer, sizeof (buffer)); +} + +// +// DANGER, WILL ROBINSON! 'value' is the desired temperature * 2, where +// the low bit indicates 0.5C or not. So for -50C degrees, we see -100. +// For -50.5C, we see -101. +// +static int lm75Read16 (U8 reg, int *value) +{ + U8 buffer [2]; + + buffer [0] = lm75LastRegister = reg; + + if (!lm75Mode) + { + if (i2cWriteReadBuffer (lm75Address, buffer, sizeof (U8), sizeof (buffer))) + return -1; + } + else + { + if (i2cWriteBuffer (lm75Address, (U8 *) ®, sizeof (U8))) + return -1; + + if (i2cReadBuffer (lm75Address, buffer, sizeof (buffer))) + return -1; + } + + // + // Sign extend negative numbers + // + *value = ((buffer [0] << 8) | buffer [1]) >> 7; + + if (buffer [0] & 0x80) + *value |= 0xfffffe00; + + return I2CERR_NONE; +} + +static int lm75Write16 (U8 reg, int value) +{ + U8 buffer [3]; + + value <<= 7; + + buffer [0] = lm75LastRegister = reg; + buffer [1] = value >> 8; + buffer [2] = value; + + return i2cWriteBuffer (lm75Address, buffer, sizeof (buffer)); +} + +// +// Don't call lm75Init() before tasker is started. The I2C routines use the +// newlib/syscalls.c _times function, which calls xTaskGetTickCount(), which +// hasn't been initialized yet. +// +int lm75Init (void) +{ + return lm75ConfigWrite (0); +} + +void lm75SetMode (int mode) +{ + lm75Mode = mode; +} + +void lm75SetAddress (U8 address) +{ + lm75Address = address; +} + +int lm75ReRead (int *value) +{ + *value = 0; + + if (lm75LastRegister == LM75_REGISTER_CONFIGURATION) + return i2cReadBuffer (lm75Address, (U8 *) value, sizeof (U8)); + else + { + U8 buffer [2]; + + if (i2cReadBuffer (lm75Address, buffer, sizeof (buffer))) + return -1; + + *value = ((buffer [0] << 8) | buffer [1]) >> 7; + } + + return I2CERR_NONE; +} + +int lm75TemperatureRead (int *temp) +{ + return lm75Read16 (LM75_REGISTER_TEMPERATURE, temp); +} + +int lm75ConfigRead (int *configValue) +{ + return lm75Read8 (LM75_REGISTER_CONFIGURATION, configValue); +} + +int lm75ConfigWrite (int configValue) +{ + return lm75Write8 (LM75_REGISTER_CONFIGURATION, configValue); +} + +int lm75THYSTRead (int *thystValue) +{ + return lm75Read16 (LM75_REGISTER_THYST, thystValue); +} + +int lm75THYSTWrite (int thystValue) +{ + return lm75Write16 (LM75_REGISTER_THYST, thystValue); +} + +int lm75TOSTRead (int *thystValue) +{ + return lm75Read16 (LM75_REGISTER_TOS, thystValue); +} + +int lm75TOSWrite (int thystValue) +{ + return lm75Write16 (LM75_REGISTER_TOS, thystValue); +} diff --git a/i2c/lm75.h b/i2c/lm75.h new file mode 100644 index 0000000..d51fdfd --- /dev/null +++ b/i2c/lm75.h @@ -0,0 +1,29 @@ +#ifndef _LM75_H_ +#define _LM75_H_ + +// +// +// +#define LM75_ADDRESS (0x90) + +#define LM75_REGISTER_TEMPERATURE (0x00) +#define LM75_REGISTER_CONFIGURATION (0x01) +#define LM75_REGISTER_THYST (0x02) +#define LM75_REGISTER_TOS (0x03) + +// +// +// +int lm75Init (void); +void lm75SetMode (int mode); +void lm75SetAddress (U8 address); +int lm75ReRead (int *value); +int lm75TemperatureRead (int *temp); +int lm75ConfigRead (int *configValue); +int lm75ConfigWrite (int configValue); +int lm75THYSTRead (int *thystValue); +int lm75THYSTWrite (int thystValue); +int lm75TOSTRead (int *thystValue); +int lm75TOSWrite (int thystValue); + +#endif diff --git a/iap/Makefile b/iap/Makefile new file mode 100644 index 0000000..a792c99 --- /dev/null +++ b/iap/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=iap.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/iap/iap.c b/iap/iap.c new file mode 100644 index 0000000..35bd67c --- /dev/null +++ b/iap/iap.c @@ -0,0 +1,537 @@ +#include "FreeRTOS.h" + +#include +#include "../cpu/cpu.h" +#include "iap.h" + +// +// +// +#define CPUCLK_IN_KHZ 48000 + +#define IRQ_MASK 0x00000080 +#define FIQ_MASK 0x00000040 +#define INTs_MASK (IRQ_MASK | FIQ_MASK) + +// +// +// +typedef struct flashSectorToAddress_s +{ + unsigned long address; + int sizeInBytes; +} +flashSectorToAddress_t; + +static flashSectorToAddress_t flashSectorToAddress [] = +{ + { 0x00000000, 4096 }, // 0 + { 0x00001000, 4096 }, // 1 + { 0x00002000, 4096 }, // 2 + { 0x00003000, 4096 }, // 3 + { 0x00004000, 4096 }, // 4 + { 0x00005000, 4096 }, // 5 + { 0x00006000, 4096 }, // 6 + { 0x00007000, 4096 }, // 7 + { 0x00008000, 32768 }, // 8 + { 0x00010000, 32768 }, // 9 + { 0x00018000, 32768 }, // 10 + { 0x00020000, 32768 }, // 11 + { 0x00028000, 32768 }, // 12 + { 0x00030000, 32768 }, // 13 + { 0x00038000, 32768 }, // 14 + { 0x00040000, 32768 }, // 15 + { 0x00048000, 32768 }, // 16 + { 0x00050000, 32768 }, // 17 + { 0x00058000, 32768 }, // 18 + { 0x00060000, 32768 }, // 19 + { 0x00068000, 32768 }, // 20 + { 0x00070000, 32768 }, // 21 + { 0x00078000, 4096 }, // 22 + { 0x00079000, 4096 }, // 23 + { 0x0007a000, 4096 }, // 24 + { 0x0007b000, 4096 }, // 25 + { 0x0007c000, 4096 }, // 26 +}; + +typedef struct iapErrnoStr_s +{ + int errno; + const char *text; +} +iapErrnoStr_t; + +static iapErrnoStr_t iapErrnoStr [] = +{ + { IAP_RESULT_CMD_SUCCESS, "success" }, + { IAP_RESULT_INVALID_COMMAND, "invalid command" }, + { IAP_RESULT_SRC_ADDR_ERROR, "source address error" }, + { IAP_RESULT_DST_ADDR_ERROR, "destination address error" }, + { IAP_RESULT_SRC_ADDR_NOT_MAPPED, "source address not mapped" }, + { IAP_RESULT_DST_ADDR_NOT_MAPPED, "destination address not mapped" }, + { IAP_RESULT_COUNT_ERROR, "count error" }, + { IAP_RESULT_INVALID_SECTOR, "invalid sector" }, + { IAP_RESULT_SECTOR_NOT_BLANK, "sector not blank" }, + { IAP_RESULT_SECTOR_NOT_PREPARED, "sector not prepared" }, + { IAP_RESULT_COMPARE_ERROR, "compare error" }, + { IAP_RESULT_BUSY, "busy" }, + { IAP_RESULT_PARAM_ERROR, "parameter error" }, + { IAP_RESULT_ADDR_ERROR, "address error" }, + { IAP_RESULT_ADDR_NOT_MAPPED, "address not mapped" }, + { IAP_RESULT_CMD_LOCKED, "command locked" }, + { IAP_RESULT_INVALID_CODE, "invalid code" }, + { IAP_RESULT_INVALID_BAUD_RATE, "invalid baud rate" }, + { IAP_RESULT_ANVALID_STOP_BIT, "invalid stop bit" }, + { IAP_RESULT_CRP_ENABLED, "CRP enabled" }, + { IAP_RESULT_X_NOTSAFEREGION, "sector or address not in safe region" }, + { IAP_RESULT_X_NOSAFEREGIONAVAIL, "no safe sectors available (all of memory used?)" }, +}; + +// +// +// +static unsigned int iapCommands [5]; +static unsigned int iapResults [2]; +static int iapErrno = 0; +extern unsigned long __end_of_text__; +static unsigned int end_of_text = (unsigned int) &__end_of_text__; + + +// +// +// +void iapInit (void) +{ +} + +// +// +// +int iapSectorToAddress (int sectorNumber, unsigned long *address, int *sectorSize) +{ + if (sectorNumber >= (int) arrsizeof (flashSectorToAddress)) + return -1; + + if (address) + *address = flashSectorToAddress [sectorNumber].address; + if (sectorSize) + *sectorSize = flashSectorToAddress [sectorNumber].sizeInBytes; + + return 0; +} + +// +// Convert address to sector, or -1 if address not in flash area +// +int iapAddressToSector (unsigned long address) +{ + int i; + + for (i = 0; i < (int) arrsizeof (flashSectorToAddress); i++) + if (address < (flashSectorToAddress [i].address + flashSectorToAddress [i].sizeInBytes)) + return i; + + iapErrno = IAP_RESULT_INVALID_SECTOR; + return -1; +} + +// +// 1 == address in safe region, 0 if not +// +int iapIsSafeAddress (unsigned long address) +{ + int eotSector; + int addressSector; + + if ((eotSector = iapAddressToSector (end_of_text)) == -1) + return 0; + if ((addressSector = iapAddressToSector (address)) == -1) + return 0; + + if (addressSector <= eotSector) + { + iapErrno = IAP_RESULT_X_NOTSAFEREGION; + return 0; + } + + return 1; +} + +// +// 0 == not safe sector, 1 == safe (not in a code sector) +// +int iapIsSafeSector (int sector) +{ + int eotSector; + + if ((eotSector = iapAddressToSector (end_of_text)) == -1) + return 0; + + if (sector <= eotSector) + { + iapErrno = IAP_RESULT_X_NOTSAFEREGION; + return 0; + } + + return 1; +} + +// +// Returns a safe sector number or -1 if none available +// +int iapFindSafeSector (void) +{ + unsigned int i; + + for (i = 0; i < arrsizeof (flashSectorToAddress); i++) + if (iapIsSafeSector (i)) + return i; + + iapErrno = IAP_RESULT_X_NOSAFEREGIONAVAIL; + return -1; +} + +// +// 1 == sector is safe, 0 == not safe +// +int iapIsValidSector (int sector) +{ + if ((sector < 0) || (sector >= (int) arrsizeof (flashSectorToAddress))) + { + iapErrno = IAP_RESULT_INVALID_SECTOR; + return 0; + } + + return 1; +} + +// +// +// +int iapGetErrno (void) +{ + return iapErrno; +} + +const char *iapStrerror (int err) +{ + unsigned int i; + + for (i = 0; i < arrsizeof (iapErrnoStr); i++) + if (iapErrnoStr [i].errno == err) + return iapErrnoStr [i].text; + + return NULL; +} + +// +// +// +static void iapCall (void) __attribute ((naked)); +static void iapCall (void) +{ + register void *pFP0 asm("r0") = iapCommands; + register void *pFP1 asm("r1") = iapResults; + + asm volatile(" bx %[iapLocation]" + : + : "r" (pFP0), "r" (pFP1), [iapLocation] "r" (IAP_LOCATION) ); +} + +// +// +// +static inline unsigned __get_cpsr (void) +{ + unsigned long retval; + + asm volatile (" mrs %0, cpsr" : "=r" (retval) : /* no inputs */ ); + + return retval; +} + +static inline void __set_cpsr (unsigned val) +{ + asm volatile (" msr cpsr, %0" : /* no outputs */ : "r" (val) ); +} + +static unsigned disableInts (void) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr (); + + do + __set_cpsr (_cpsr | INTs_MASK); + while ((__get_cpsr () ^ INTs_MASK) & INTs_MASK); + + return _cpsr; +} + +static unsigned restoreInts (unsigned oldCPSR) +{ + unsigned _cpsr; + + _cpsr = __get_cpsr(); + + do + __set_cpsr ((_cpsr & ~INTs_MASK) | (oldCPSR & INTs_MASK)); + while ((__get_cpsr () ^ oldCPSR) & INTs_MASK); + + return _cpsr; +} + +// +// +// +int iapPrepareSectors (int startingSector, int endingSector) +{ + unsigned int cpsr; + + if (!iapIsSafeSector (startingSector) || !iapIsSafeSector (endingSector)) + return -1; + + iapCommands [0] = IAP_CMD_PREPARE; + iapCommands [1] = startingSector; + iapCommands [2] = endingSector; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + return ((iapErrno = iapResults [0]) == IAP_RESULT_CMD_SUCCESS) ? 0 : -1; +} + +// +// IAP_CMD_COPYRAMTOFLASH can span multiple sectors (2, at any rate, since bufferLen +// must be 256, 512, 1024 or 4096, and the smallest sectors are 4096 bytes). Although +// more than 2 sectors can be prepared for writing, it's useless to do so, since +// after each IAP_CMD_COPYRAMTOFLASH, the sectors are re-locked. +// +int iapWriteSectors (unsigned int address, unsigned char *buffer, int bufferLen) +{ + unsigned int cpsr; + + iapCommands [0] = IAP_CMD_COPYRAMTOFLASH; + iapCommands [1] = address; + iapCommands [2] = (int) buffer; + iapCommands [3] = bufferLen; + iapCommands [4] = CPUCLK_IN_KHZ; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + if ((iapErrno = iapResults [0]) != IAP_RESULT_CMD_SUCCESS) + return -1; + + return 0; +} + +int iapFillSectors (int startingSector, int endingSector, int byte) +{ + int sector; + unsigned char buffer [256]; + + if (!iapIsSafeSector (startingSector) || !iapIsSafeSector (endingSector)) + return -1; + + memset (buffer, byte, sizeof (buffer)); + + for (sector = startingSector; sector <= endingSector; sector++) + { + int i; + unsigned long address; + int sectorSize; + + if (iapSectorToAddress (sector, &address, §orSize)) + return -1; + + for (i = 0; i < sectorSize; i += sizeof (buffer)) + { + if (iapPrepareSectors (sector, sector) == -1) + return -1; + if (iapWriteSectors (address + i, buffer, sizeof (buffer)) == -1) + return -1; + if (iapCompare (address + i, buffer, sizeof (buffer)) == -1) + return -1; + } + } + + return 0; +} + +int iapEraseSectors (int startingSector, int endingSector) +{ + unsigned int cpsr; + + if (!iapIsSafeSector (startingSector) || !iapIsSafeSector (endingSector)) + return -1; + + if (iapPrepareSectors (startingSector, endingSector) == -1) + return -1; + + iapCommands [0] = IAP_CMD_ERASE; + iapCommands [1] = startingSector; + iapCommands [2] = endingSector; + iapCommands [3] = CPUCLK_IN_KHZ; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + return ((iapErrno = iapResults [0]) == IAP_RESULT_CMD_SUCCESS) ? 0 : -1; +} + +// +// -1 = error (iapErrno set) +// 0 = sector blank +// 1 = sector not blank +// +int iapBlankCheckSectors (int startingSector, int endingSector) +{ + unsigned int cpsr; + + if (!iapIsValidSector (startingSector) || !iapIsValidSector (endingSector)) + return -1; + + iapCommands [0] = IAP_CMD_BLANKCHECK; + iapCommands [1] = startingSector; + iapCommands [2] = endingSector; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + if ((iapErrno = iapResults [0]) == IAP_RESULT_CMD_SUCCESS) + return 0; + if (iapResults [0] == IAP_RESULT_SECTOR_NOT_BLANK) + return 1; + + return -1; +} + +// +// +// +unsigned int iapReadPartID (void) +{ + unsigned int cpsr; + + iapCommands [0] = IAP_CMD_READPARTID; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + return iapResults [1]; +} + +// +// +// +unsigned int iapReadBootCodeVersion (void) +{ + unsigned int cpsr; + + iapCommands [0] = IAP_CMD_READBOOTCODEVER; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + return iapResults [1]; +} + +// +// +// +int iapCompare (unsigned int address, unsigned char *buffer, int bufferLen) +{ + unsigned int cpsr; + + iapCommands [0] = IAP_CMD_COMPARE; + iapCommands [1] = address; + iapCommands [2] = (int) buffer; + iapCommands [3] = bufferLen; + + cpsr = disableInts (); + + iapCall (); + + restoreInts (cpsr); + + return ((iapErrno = iapResults [0]) == IAP_RESULT_CMD_SUCCESS) ? 0 : -1; +} + +// +// +// +void iapISP (void) +{ + int i; + REG32 *p; + + portDISABLE_INTERRUPTS (); + + VIC_IntEnClr = VIC_IntEnClr_MASK; + + SCB_PLLCON = 0; + SCB_PLLCFG = 0; + SCB_PLLFEED = SCB_PLLFEED_FEED1; + SCB_PLLFEED = SCB_PLLFEED_FEED2; + + USB_PLLCON = 0; + USB_PLLCFG = 0; + USB_PLLFEED = USB_PLLFEED_FEED1; + USB_PLLFEED = USB_PLLFEED_FEED2; + + for (p = (REG32 *) T1_BASE_ADDR, i = 0; i< 0x74 / 4; i++) + *p++ = 0; + + T1_IR = 0xff; + + SCB_PCONP = 0x000003be; + + UART0_IER = 0x00; + UART0_LCR = 0x80; + UART0_DLL = 0x01; + UART0_DLM = 0x00; + UART0_LCR = 0x00; + UART0_FCR = 0x06; + UART0_FDR = 0x10; + + SCB_MEMMAP = 0; + SCB_VPBDIV = 0; + + PCB_PINSEL0 = 0x00000000; + PCB_PINSEL1 = 0x00000000; + + GPIO0_IODIR = 0x00004000; + GPIO0_IOCLR = 0x00004000; + + WD_TC = 0x0ffffff; + WD_MOD = 0; + WD_FEED = WD_FEED_FEED1; + WD_FEED = WD_FEED_FEED2; + + iapCommands [0] = IAP_CMD_REINVOKEISP; + + // disableInts (); + // cpuPLLDisable (); + // cpuT1Disable (); + + iapCall (); +} diff --git a/iap/iap.h b/iap/iap.h new file mode 100644 index 0000000..6b52701 --- /dev/null +++ b/iap/iap.h @@ -0,0 +1,32 @@ +#ifndef _IAP_H_ +#define _IAP_H_ + +// +// +// +#define IAP_RESULT_X_NOTSAFEREGION (IAP_RESULT_LAST + 1) +#define IAP_RESULT_X_NOSAFEREGIONAVAIL (IAP_RESULT_LAST + 2) + +// +// +// +void iapInit (void); +int iapSectorToAddress (int sectorNumber, unsigned long *address, int *sectorSize); +int iapAddressToSector (unsigned long address); +int iapIsSafeAddress (unsigned long address);; +int iapIsSafeSector (int sector); +int iapFindSafeSector (void); +int iapIsValidSector (int sector); +int iapGetErrno (void); +const char *iapStrerror (int err); +int iapPrepareSectors (int startingSector, int endingSector); +int iapFillSectors (int startingSector, int endingSector, int byte); +int iapWriteSectors (unsigned int address, unsigned char *buffer, int bufferLen); +int iapEraseSectors (int startingSector, int endingSector); +int iapBlankCheckSectors (int startingSector, int endingSector); +unsigned int iapReadPartID (void); +unsigned int iapReadBootCodeVersion (void); +int iapCompare (unsigned int address, unsigned char *buffer, int bufferLen); +void iapISP (void); + +#endif diff --git a/leds/Makefile b/leds/Makefile new file mode 100644 index 0000000..984efe2 --- /dev/null +++ b/leds/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=leds.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/leds/leds.c b/leds/leds.c new file mode 100644 index 0000000..15d1cd0 --- /dev/null +++ b/leds/leds.c @@ -0,0 +1,77 @@ +// +// +// +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "../cpu/cpu.h" +#include "leds.h" + +// +// +// +typedef struct +{ + int timeOn; + int timeOff; +} +ledDutyCycles_t; + +static ledDutyCycles_t ledDutyCycles [] = +{ + { (200 / portTICK_RATE_MS), (800 / portTICK_RATE_MS) }, + { (400 / portTICK_RATE_MS), (600 / portTICK_RATE_MS) }, + { (600 / portTICK_RATE_MS), (400 / portTICK_RATE_MS) }, + { (800 / portTICK_RATE_MS), (200 / portTICK_RATE_MS) }, +}; + +xQueueHandle xLEDQueue; + +// +// +// +portTASK_FUNCTION (vLEDFlashTask, pvParameters __attribute__ ((unused))) +{ + portTickType ledTimeOn = 1; + portTickType ledTimeOff = 1; + portTickType lastTickTime; + int dutyCycle = 0; + + // + // Create the queue, turn on LED and die if we can't + // + if ((xLEDQueue = xQueueCreate (1, sizeof (dutyCycle))) == 0) + { + GPIO0_IOSET = GPIO_IO_P10; + + while (1) + vTaskDelay (100); + } + + // + // Send ourselves a message to init the flash time + // + xQueueSend (xLEDQueue, &dutyCycle, (portTickType) 0); + + // + // We need to initialise lastTickTime prior to the first call to vTaskDelayUntil() + // + lastTickTime = xTaskGetTickCount (); + + for (;;) + { + vTaskDelayUntil (&lastTickTime, ledTimeOn); + GPIO0_IOSET = GPIO_IO_P10; + vTaskDelayUntil (&lastTickTime, ledTimeOff); + GPIO0_IOCLR = GPIO_IO_P10; + + if (xQueueReceive (xLEDQueue, &dutyCycle, (portTickType) 0)) + { + dutyCycle %= arrsizeof (ledDutyCycles); + + ledTimeOn = ledDutyCycles [dutyCycle].timeOn; + ledTimeOff = ledDutyCycles [dutyCycle].timeOff; + } + } +} diff --git a/leds/leds.h b/leds/leds.h new file mode 100644 index 0000000..fd37529 --- /dev/null +++ b/leds/leds.h @@ -0,0 +1,14 @@ +#ifndef _LEDS_H_ +#define _LEDS_H_ + +// +// +// +extern xQueueHandle xLEDQueue; + +// +// +// +portTASK_FUNCTION (vLEDFlashTask, pvParameters __attribute__ ((unused))); + +#endif diff --git a/leds/leds_reuse.c b/leds/leds_reuse.c new file mode 100644 index 0000000..07761e4 --- /dev/null +++ b/leds/leds_reuse.c @@ -0,0 +1,58 @@ +// +// This code was from the original demo package. Although it is no longer +// used, it shows an excellent example of how to reuse task code with +// local variables. +// +// Each time the task is create with xTaskCreate, it increment the task +// number, which it uses to calculate a flash rate and which LED to +// flash. +// +#include "FreeRTOS.h" +#include "task.h" + +#include "../cpu/cpu.h" +#include "leds.h" + +// +// +// +#define ledFLASH_RATE_BASE ((portTickType) 333) +static portBASE_TYPE uxFlashTaskNumber = 0; + +portTASK_FUNCTION (vLEDFlashTask, pvParameters __attribute__ ((unused))) +{ + portTickType xFlashRate; + portTickType xLastFlashTime; + unsigned portBASE_TYPE uxLED; + + // + // Calculate the LED and flash rate + // + portENTER_CRITICAL (); + { + uxLED = uxFlashTaskNumber; + uxFlashTaskNumber++; + } + portEXIT_CRITICAL (); + + xFlashRate = ledFLASH_RATE_BASE + (ledFLASH_RATE_BASE * (portTickType) uxLED); + xFlashRate /= portTICK_RATE_MS; + + // + // We will turn the LED on and off again in the delay period, so each delay is only half the total period + // + xFlashRate /= (portTickType) 2; + + // + // We need to initialise xLastFlashTime prior to the first call to vTaskDelayUntil() + // + xLastFlashTime = xTaskGetTickCount(); + + for (;;) + { + vTaskDelayUntil (&xLastFlashTime, xFlashRate); + cpuToggleLED (uxLED); + vTaskDelayUntil (&xLastFlashTime, xFlashRate); + cpuToggleLED (uxLED); + } +} diff --git a/lpc210x.h b/lpc210x.h new file mode 100644 index 0000000..f740f73 --- /dev/null +++ b/lpc210x.h @@ -0,0 +1,2310 @@ +/* +// Using typedef'ed structures instead of #define's would be much cleaner. However, +// it's a damn lot of work to define them all, and there were a bunch of #defines +// to start with, so I just expanded them. +*/ + +#ifndef _LPC210X_H_ +#define _LPC210X_H_ + +#include "sysdefs.h" + +/*############################################################################## +## MISC +##############################################################################*/ + +/* Constants for data to put in IRQ/FIQ Exception Vectors */ +#define VECTDATA_IRQ 0xe51ffff0 /* LDR PC,[PC,#-0xFF0] */ +#define VECTDATA_FIQ /* __TODO */ + + +/*############################################################################## +## VECTORED INTERRUPT CONTROLLER +##############################################################################*/ + +#define VIC_IRQStatus (*(pREG32 (0xfffff000))) +#define VIC_FIQStatus (*(pREG32 (0xfffff004))) +#define VIC_RawIntr (*(pREG32 (0xfffff008))) +#define VIC_IntSelect (*(pREG32 (0xfffff00c))) +#define VIC_IntEnable (*(pREG32 (0xfffff010))) +#define VIC_IntEnClr (*(pREG32 (0xfffff014))) +#define VIC_SoftInt (*(pREG32 (0xfffff018))) +#define VIC_SoftIntClear (*(pREG32 (0xfffff01c))) +#define VIC_Protection (*(pREG32 (0xfffff020))) +#define VIC_VectAddr (*(pREG32 (0xfffff030))) +#define VIC_DefVectAddr (*(pREG32 (0xfffff034))) + +#define VIC_VectAddr0 (*(pREG32 (0xfffff100))) +#define VIC_VectAddr1 (*(pREG32 (0xfffff104))) +#define VIC_VectAddr2 (*(pREG32 (0xfffff108))) +#define VIC_VectAddr3 (*(pREG32 (0xfffff10c))) +#define VIC_VectAddr4 (*(pREG32 (0xfffff110))) +#define VIC_VectAddr5 (*(pREG32 (0xfffff114))) +#define VIC_VectAddr6 (*(pREG32 (0xfffff118))) +#define VIC_VectAddr7 (*(pREG32 (0xfffff11c))) +#define VIC_VectAddr8 (*(pREG32 (0xfffff120))) +#define VIC_VectAddr9 (*(pREG32 (0xfffff124))) +#define VIC_VectAddr10 (*(pREG32 (0xfffff128))) +#define VIC_VectAddr11 (*(pREG32 (0xfffff12c))) +#define VIC_VectAddr12 (*(pREG32 (0xfffff130))) +#define VIC_VectAddr13 (*(pREG32 (0xfffff134))) +#define VIC_VectAddr14 (*(pREG32 (0xfffff138))) +#define VIC_VectAddr15 (*(pREG32 (0xfffff13c))) + +#define VIC_VectCntl0 (*(pREG32 (0xfffff200))) +#define VIC_VectCntl1 (*(pREG32 (0xfffff204))) +#define VIC_VectCntl2 (*(pREG32 (0xfffff208))) +#define VIC_VectCntl3 (*(pREG32 (0xfffff20c))) +#define VIC_VectCntl4 (*(pREG32 (0xfffff210))) +#define VIC_VectCntl5 (*(pREG32 (0xfffff214))) +#define VIC_VectCntl6 (*(pREG32 (0xfffff218))) +#define VIC_VectCntl7 (*(pREG32 (0xfffff21c))) +#define VIC_VectCntl8 (*(pREG32 (0xfffff220))) +#define VIC_VectCntl9 (*(pREG32 (0xfffff224))) +#define VIC_VectCntl10 (*(pREG32 (0xfffff228))) +#define VIC_VectCntl11 (*(pREG32 (0xfffff22c))) +#define VIC_VectCntl12 (*(pREG32 (0xfffff230))) +#define VIC_VectCntl13 (*(pREG32 (0xfffff234))) +#define VIC_VectCntl14 (*(pREG32 (0xfffff238))) +#define VIC_VectCntl15 (*(pREG32 (0xfffff23c))) + +#define VIC_ITCR (*(pREG32 (0xfffff300))) +#define VIC_ITIP1 (*(pREG32 (0xfffff304))) +#define VIC_ITIP2 (*(pREG32 (0xfffff308))) +#define VIC_ITOP1 (*(pREG32 (0xfffff30c))) +#define VIC_ITOP2 (*(pREG32 (0xfffff310))) +#define VIC_PeriphID0 (*(pREG32 (0xffffffe0))) +#define VIC_PeriphID1 (*(pREG32 (0xffffffe4))) +#define VIC_PeriphID2 (*(pREG32 (0xffffffe8))) +#define VIC_PeriphID3 (*(pREG32 (0xffffffec))) + +#define VIC_SoftInt_WDT (0x00000001) +#define VIC_SoftInt_ARMCore0 (0x00000004) +#define VIC_SoftInt_ARMCore1 (0x00000008) +#define VIC_SoftInt_Timer0 (0x00000010) +#define VIC_SoftInt_Timer1 (0x00000020) +#define VIC_SoftInt_UART0 (0x00000040) +#define VIC_SoftInt_UART1 (0x00000080) +#define VIC_SoftInt_PWM0 (0x00000100) +#define VIC_SoftInt_I2C0 (0x00000200) +#define VIC_SoftInt_SPI0 (0x00000400) +#define VIC_SoftInt_SPI1 (0x00000800) +#define VIC_SoftInt_SSP (0x00000800) +#define VIC_SoftInt_PLL (0x00001000) +#define VIC_SoftInt_RTC (0x00002000) +#define VIC_SoftInt_EINT0 (0x00004000) +#define VIC_SoftInt_EINT1 (0x00008000) +#define VIC_SoftInt_EINT2 (0x00010000) +#define VIC_SoftInt_EINT3 (0x00020000) +#define VIC_SoftInt_AD0 (0x00040000) +#define VIC_SoftInt_I2C1 (0x00080000) +#define VIC_SoftInt_BOD (0x00100000) +#define VIC_SoftInt_AD1 (0x00200000) +#define VIC_SoftInt_USB (0x00400000) +#define VIC_SoftInt_MASK (0x007ffffd) + +#define VIC_SoftIntClr_WDT (0x00000001) +#define VIC_SoftIntClr_ARMCore0 (0x00000004) +#define VIC_SoftIntClr_ARMCore1 (0x00000008) +#define VIC_SoftIntClr_Timer0 (0x00000010) +#define VIC_SoftIntClr_Timer1 (0x00000020) +#define VIC_SoftIntClr_UART0 (0x00000040) +#define VIC_SoftIntClr_UART1 (0x00000080) +#define VIC_SoftIntClr_PWM0 (0x00000100) +#define VIC_SoftIntClr_I2C0 (0x00000200) +#define VIC_SoftIntClr_SPI0 (0x00000400) +#define VIC_SoftIntClr_SPI1 (0x00000800) +#define VIC_SoftIntClr_SSP (0x00000800) +#define VIC_SoftIntClr_PLL (0x00001000) +#define VIC_SoftIntClr_RTC (0x00002000) +#define VIC_SoftIntClr_EINT0 (0x00004000) +#define VIC_SoftIntClr_EINT1 (0x00008000) +#define VIC_SoftIntClr_EINT2 (0x00010000) +#define VIC_SoftIntClr_EINT3 (0x00020000) +#define VIC_SoftIntClr_AD0 (0x00040000) +#define VIC_SoftIntClr_I2C1 (0x00080000) +#define VIC_SoftIntClr_BOD (0x00100000) +#define VIC_SoftIntClr_AD1 (0x00200000) +#define VIC_SoftIntClr_USB (0x00400000) +#define VIC_SoftIntClr_MASK (0x007ffffd) + +#define VIC_RawIntr_WDT (0x00000001) +#define VIC_RawIntr_ARMCore0 (0x00000004) +#define VIC_RawIntr_ARMCore1 (0x00000008) +#define VIC_RawIntr_Timer0 (0x00000010) +#define VIC_RawIntr_Timer1 (0x00000020) +#define VIC_RawIntr_UART0 (0x00000040) +#define VIC_RawIntr_UART1 (0x00000080) +#define VIC_RawIntr_PWM0 (0x00000100) +#define VIC_RawIntr_I2C0 (0x00000200) +#define VIC_RawIntr_SPI0 (0x00000400) +#define VIC_RawIntr_SPI1 (0x00000800) +#define VIC_RawIntr_SSP (0x00000800) +#define VIC_RawIntr_PLL (0x00001000) +#define VIC_RawIntr_RTC (0x00002000) +#define VIC_RawIntr_EINT0 (0x00004000) +#define VIC_RawIntr_EINT1 (0x00008000) +#define VIC_RawIntr_EINT2 (0x00010000) +#define VIC_RawIntr_EINT3 (0x00020000) +#define VIC_RawIntr_AD0 (0x00040000) +#define VIC_RawIntr_I2C1 (0x00080000) +#define VIC_RawIntr_BOD (0x00100000) +#define VIC_RawIntr_AD1 (0x00200000) +#define VIC_RawIntr_USB (0x00400000) +#define VIC_RawIntr_MASK (0x007ffffd) + +#define VIC_IntEnable_WDT (0x00000001) +#define VIC_IntEnable_ARMCore0 (0x00000004) +#define VIC_IntEnable_ARMCore1 (0x00000008) +#define VIC_IntEnable_Timer0 (0x00000010) +#define VIC_IntEnable_Timer1 (0x00000020) +#define VIC_IntEnable_UART0 (0x00000040) +#define VIC_IntEnable_UART1 (0x00000080) +#define VIC_IntEnable_PWM0 (0x00000100) +#define VIC_IntEnable_I2C0 (0x00000200) +#define VIC_IntEnable_SPI0 (0x00000400) +#define VIC_IntEnable_SPI1 (0x00000800) +#define VIC_IntEnable_SSP (0x00000800) +#define VIC_IntEnable_PLL (0x00001000) +#define VIC_IntEnable_RTC (0x00002000) +#define VIC_IntEnable_EINT0 (0x00004000) +#define VIC_IntEnable_EINT1 (0x00008000) +#define VIC_IntEnable_EINT2 (0x00010000) +#define VIC_IntEnable_EINT3 (0x00020000) +#define VIC_IntEnable_AD0 (0x00040000) +#define VIC_IntEnable_I2C1 (0x00080000) +#define VIC_IntEnable_BOD (0x00100000) +#define VIC_IntEnable_AD1 (0x00200000) +#define VIC_IntEnable_USB (0x00400000) +#define VIC_IntEnable_MASK (0x007ffffd) + +#define VIC_IntEnClr_WDT (0x00000001) +#define VIC_IntEnClr_ARMCore0 (0x00000004) +#define VIC_IntEnClr_ARMCore1 (0x00000008) +#define VIC_IntEnClr_Timer0 (0x00000010) +#define VIC_IntEnClr_Timer1 (0x00000020) +#define VIC_IntEnClr_UART0 (0x00000040) +#define VIC_IntEnClr_UART1 (0x00000080) +#define VIC_IntEnClr_PWM0 (0x00000100) +#define VIC_IntEnClr_I2C0 (0x00000200) +#define VIC_IntEnClr_SPI0 (0x00000400) +#define VIC_IntEnClr_SPI1 (0x00000800) +#define VIC_IntEnClr_SSP (0x00000800) +#define VIC_IntEnClr_PLL (0x00001000) +#define VIC_IntEnClr_RTC (0x00002000) +#define VIC_IntEnClr_EINT0 (0x00004000) +#define VIC_IntEnClr_EINT1 (0x00008000) +#define VIC_IntEnClr_EINT2 (0x00010000) +#define VIC_IntEnClr_EINT3 (0x00020000) +#define VIC_IntEnClr_AD0 (0x00040000) +#define VIC_IntEnClr_I2C1 (0x00080000) +#define VIC_IntEnClr_BOD (0x00100000) +#define VIC_IntEnClr_AD1 (0x00200000) +#define VIC_IntEnClr_USB (0x00400000) +#define VIC_IntEnClr_MASK (0x007ffffd) + +#define VIC_IntSelect_WDT (0x00000001) +#define VIC_IntSelect_ARMCore0 (0x00000004) +#define VIC_IntSelect_ARMCore1 (0x00000008) +#define VIC_IntSelect_Timer0 (0x00000010) +#define VIC_IntSelect_Timer1 (0x00000020) +#define VIC_IntSelect_UART0 (0x00000040) +#define VIC_IntSelect_UART1 (0x00000080) +#define VIC_IntSelect_PWM0 (0x00000100) +#define VIC_IntSelect_I2C0 (0x00000200) +#define VIC_IntSelect_SPI0 (0x00000400) +#define VIC_IntSelect_SPI1 (0x00000800) +#define VIC_IntSelect_SSP (0x00000800) +#define VIC_IntSelect_PLL (0x00001000) +#define VIC_IntSelect_RTC (0x00002000) +#define VIC_IntSelect_EINT0 (0x00004000) +#define VIC_IntSelect_EINT1 (0x00008000) +#define VIC_IntSelect_EINT2 (0x00010000) +#define VIC_IntSelect_EINT3 (0x00020000) +#define VIC_IntSelect_AD0 (0x00040000) +#define VIC_IntSelect_I2C1 (0x00080000) +#define VIC_IntSelect_BOD (0x00100000) +#define VIC_IntSelect_AD1 (0x00200000) +#define VIC_IntSelect_USB (0x00400000) +#define VIC_IntSelect_MASK (0x007ffffd) + +#define VIC_IRQStatus_WDT (0x00000001) +#define VIC_IRQStatus_ARMCore0 (0x00000004) +#define VIC_IRQStatus_ARMCore1 (0x00000008) +#define VIC_IRQStatus_Timer0 (0x00000010) +#define VIC_IRQStatus_Timer1 (0x00000020) +#define VIC_IRQStatus_UART0 (0x00000040) +#define VIC_IRQStatus_UART1 (0x00000080) +#define VIC_IRQStatus_PWM0 (0x00000100) +#define VIC_IRQStatus_I2C0 (0x00000200) +#define VIC_IRQStatus_SPI0 (0x00000400) +#define VIC_IRQStatus_SPI1 (0x00000800) +#define VIC_IRQStatus_SSP (0x00000800) +#define VIC_IRQStatus_PLL (0x00001000) +#define VIC_IRQStatus_RTC (0x00002000) +#define VIC_IRQStatus_EINT0 (0x00004000) +#define VIC_IRQStatus_EINT1 (0x00008000) +#define VIC_IRQStatus_EINT2 (0x00010000) +#define VIC_IRQStatus_EINT3 (0x00020000) +#define VIC_IRQStatus_AD0 (0x00040000) +#define VIC_IRQStatus_I2C1 (0x00080000) +#define VIC_IRQStatus_BOD (0x00100000) +#define VIC_IRQStatus_AD1 (0x00200000) +#define VIC_IRQStatus_USB (0x00400000) +#define VIC_IRQStatus_MASK (0x007ffffd) + +#define VIC_FIQStatus_WDT (0x00000001) +#define VIC_FIQStatus_ARMCore0 (0x00000004) +#define VIC_FIQStatus_ARMCore1 (0x00000008) +#define VIC_FIQStatus_Timer0 (0x00000010) +#define VIC_FIQStatus_Timer1 (0x00000020) +#define VIC_FIQStatus_UART0 (0x00000040) +#define VIC_FIQStatus_UART1 (0x00000080) +#define VIC_FIQStatus_PWM0 (0x00000100) +#define VIC_FIQStatus_I2C0 (0x00000200) +#define VIC_FIQStatus_SPI0 (0x00000400) +#define VIC_FIQStatus_SPI1 (0x00000800) +#define VIC_FIQStatus_SSP (0x00000800) +#define VIC_FIQStatus_PLL (0x00001000) +#define VIC_FIQStatus_RTC (0x00002000) +#define VIC_FIQStatus_EINT0 (0x00004000) +#define VIC_FIQStatus_EINT1 (0x00008000) +#define VIC_FIQStatus_EINT2 (0x00010000) +#define VIC_FIQStatus_EINT3 (0x00020000) +#define VIC_FIQStatus_AD0 (0x00040000) +#define VIC_FIQStatus_I2C1 (0x00080000) +#define VIC_FIQStatus_BOD (0x00100000) +#define VIC_FIQStatus_AD1 (0x00200000) +#define VIC_FIQStatus_USB (0x00400000) +#define VIC_FIQStatus_MASK (0x007ffffd) + +#define VIC_VectCntl_SLOTMASK (0x0000001f) +#define VIC_VectCntl_ENABLE (0x00000020) + +#define VIC_Protection_ACCESS (0x00000001) +#define VIC_Protection_MASK (0x00000001) + +#define VIC_Mask_WDT (0x00000001) +#define VIC_Mask_RSVD (0x00000002) +#define VIC_Mask_ARMCore0 (0x00000004) +#define VIC_Mask_ARMCore1 (0x00000008) +#define VIC_Mask_Timer0 (0x00000010) +#define VIC_Mask_Timer1 (0x00000020) +#define VIC_Mask_UART0 (0x00000040) +#define VIC_Mask_UART1 (0x00000080) +#define VIC_Mask_PWM0 (0x00000100) +#define VIC_Mask_I2C0 (0x00000200) +#define VIC_Mask_SPI0 (0x00000400) +#define VIC_Mask_SPI1 (0x00000800) +#define VIC_Mask_SSP (0x00000800) +#define VIC_Mask_PLL (0x00001000) +#define VIC_Mask_RTC (0x00002000) +#define VIC_Mask_EINT0 (0x00004000) +#define VIC_Mask_EINT1 (0x00008000) +#define VIC_Mask_EINT2 (0x00010000) +#define VIC_Mask_EINT3 (0x00020000) +#define VIC_Mask_AD0 (0x00040000) +#define VIC_Mask_I2C1 (0x00080000) +#define VIC_Mask_BOD (0x00100000) +#define VIC_Mask_AD1 (0x00200000) +#define VIC_Mask_USB (0x00400000) +#define VIC_Mask_MASK (0x007ffffd) + +#define VIC_Channel_WDT (0) +#define VIC_Channel_RSVD (1) +#define VIC_Channel_ARMCore0 (2) +#define VIC_Channel_ARMCore1 (3) +#define VIC_Channel_Timer0 (4) +#define VIC_Channel_Timer1 (5) +#define VIC_Channel_UART0 (6) +#define VIC_Channel_UART1 (7) +#define VIC_Channel_PWM0 (8) +#define VIC_Channel_I2C0 (9) +#define VIC_Channel_SPI0 (10) +#define VIC_Channel_SPI1 (11) +#define VIC_Channel_SSP (11) +#define VIC_Channel_PLL (12) +#define VIC_Channel_RTC (13) +#define VIC_Channel_EINT0 (14) +#define VIC_Channel_EINT1 (15) +#define VIC_Channel_EINT2 (16) +#define VIC_Channel_EINT3 (17) +#define VIC_Channel_AD0 (18) +#define VIC_Channel_I2C1 (19) +#define VIC_Channel_BOD (20) +#define VIC_Channel_AD1 (21) +#define VIC_Channel_USB (22) + + +/*############################################################################## +## PCB - Pin Connect Block +##############################################################################*/ + +#define PCB_PINSEL0 (*(pREG32 (0xe002c000))) +#define PCB_PINSEL1 (*(pREG32 (0xe002c004))) + +#define PCB_PINSEL0_P00_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P00_TXD0 ((unsigned int) 0x00000001) +#define PCB_PINSEL0_P00_PWM1 ((unsigned int) 0x00000002) +#define PCB_PINSEL0_P00_RSVD3 ((unsigned int) 0x00000003) +#define PCB_PINSEL0_P00_MASK ((unsigned int) 0x00000003) + +#define PCB_PINSEL0_P01_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P01_RXD0 ((unsigned int) 0x00000004) +#define PCB_PINSEL0_P01_PWM3 ((unsigned int) 0x00000008) +#define PCB_PINSEL0_P01_EINT0 ((unsigned int) 0x0000000c) +#define PCB_PINSEL0_P01_MASK ((unsigned int) 0x0000000c) + +#define PCB_PINSEL0_P02_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P02_SCL0 ((unsigned int) 0x00000010) +#define PCB_PINSEL0_P02_CAP00 ((unsigned int) 0x00000020) +#define PCB_PINSEL0_P02_RSVD3 ((unsigned int) 0x00000030) +#define PCB_PINSEL0_P02_MASK ((unsigned int) 0x00000030) + +#define PCB_PINSEL0_P03_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P03_SDA0 ((unsigned int) 0x00000040) +#define PCB_PINSEL0_P03_MAT00 ((unsigned int) 0x00000080) +#define PCB_PINSEL0_P03_EINT1 ((unsigned int) 0x000000c0) +#define PCB_PINSEL0_P03_MASK ((unsigned int) 0x000000c0) + +#define PCB_PINSEL0_P04_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P04_SCK0 ((unsigned int) 0x00000100) +#define PCB_PINSEL0_P04_CAP01 ((unsigned int) 0x00000200) +#define PCB_PINSEL0_P04_RSVD3 ((unsigned int) 0x00000300) +#define PCB_PINSEL0_P04_MASK ((unsigned int) 0x00000300) + +#define PCB_PINSEL0_P05_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P05_MISO0 ((unsigned int) 0x00000400) +#define PCB_PINSEL0_P05_MAT01 ((unsigned int) 0x00000800) +#define PCB_PINSEL0_P05_AD06 ((unsigned int) 0x00000c00) +#define PCB_PINSEL0_P05_MASK ((unsigned int) 0x00000c00) + +#define PCB_PINSEL0_P06_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P06_MOSI0 ((unsigned int) 0x00001000) +#define PCB_PINSEL0_P06_CAP02 ((unsigned int) 0x00002000) +#define PCB_PINSEL0_P06_AD10 ((unsigned int) 0x00003000) +#define PCB_PINSEL0_P06_MASK ((unsigned int) 0x00003000) + +#define PCB_PINSEL0_P07_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P07_SSEL0 ((unsigned int) 0x00004000) +#define PCB_PINSEL0_P07_PWM2 ((unsigned int) 0x00008000) +#define PCB_PINSEL0_P07_EINT2 ((unsigned int) 0x0000c000) +#define PCB_PINSEL0_P07_MASK ((unsigned int) 0x0000c000) + +#define PCB_PINSEL0_P08_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P08_TXD1 ((unsigned int) 0x00010000) +#define PCB_PINSEL0_P08_PWM4 ((unsigned int) 0x00020000) +#define PCB_PINSEL0_P08_AD11 ((unsigned int) 0x00030000) +#define PCB_PINSEL0_P08_MASK ((unsigned int) 0x00030000) + +#define PCB_PINSEL0_P09_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P09_RXD1 ((unsigned int) 0x00040000) +#define PCB_PINSEL0_P09_PWM6 ((unsigned int) 0x00080000) +#define PCB_PINSEL0_P09_EINT3 ((unsigned int) 0x000c0000) +#define PCB_PINSEL0_P09_MASK ((unsigned int) 0x000c0000) + +#define PCB_PINSEL0_P010_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P010_RTS1 ((unsigned int) 0x00100000) +#define PCB_PINSEL0_P010_CAP10 ((unsigned int) 0x00200000) +#define PCB_PINSEL0_P010_AD12 ((unsigned int) 0x00300000) +#define PCB_PINSEL0_P010_MASK ((unsigned int) 0x00300000) + +#define PCB_PINSEL0_P011_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P011_CTS1 ((unsigned int) 0x00400000) +#define PCB_PINSEL0_P011_CAP11 ((unsigned int) 0x00800000) +#define PCB_PINSEL0_P011_SCL1 ((unsigned int) 0x00c00000) +#define PCB_PINSEL0_P011_MASK ((unsigned int) 0x00c00000) + +#define PCB_PINSEL0_P012_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P012_DSR1 ((unsigned int) 0x01000000) +#define PCB_PINSEL0_P012_MAT10 ((unsigned int) 0x02000000) +#define PCB_PINSEL0_P012_AD13 ((unsigned int) 0x03000000) +#define PCB_PINSEL0_P012_MASK ((unsigned int) 0x03000000) + +#define PCB_PINSEL0_P013_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P013_DTR1 ((unsigned int) 0x04000000) +#define PCB_PINSEL0_P013_MAT11 ((unsigned int) 0x08000000) +#define PCB_PINSEL0_P013_AD14 ((unsigned int) 0x0c000000) +#define PCB_PINSEL0_P013_MASK ((unsigned int) 0x0c000000) + +#define PCB_PINSEL0_P014_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P014_DCD1 ((unsigned int) 0x10000000) +#define PCB_PINSEL0_P014_EINT1 ((unsigned int) 0x20000000) +#define PCB_PINSEL0_P014_SDA1 ((unsigned int) 0x30000000) +#define PCB_PINSEL0_P014_MASK ((unsigned int) 0x30000000) + +#define PCB_PINSEL0_P015_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL0_P015_RI1 ((unsigned int) 0x40000000) +#define PCB_PINSEL0_P015_EINT2 ((unsigned int) 0x80000000) +#define PCB_PINSEL0_P015_AD15 ((unsigned int) 0xc0000000) +#define PCB_PINSEL0_P015_MASK ((unsigned int) 0xc0000000) + +#define PCB_PINSEL1_P016_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P016_EINT0 ((unsigned int) 0x00000001) +#define PCB_PINSEL1_P016_MAT02 ((unsigned int) 0x00000002) +#define PCB_PINSEL1_P016_CAP02 ((unsigned int) 0x00000003) +#define PCB_PINSEL1_P016_MASK ((unsigned int) 0x00000003) + +#define PCB_PINSEL1_P017_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P017_CAP12 ((unsigned int) 0x00000004) +#define PCB_PINSEL1_P017_SCK1 ((unsigned int) 0x00000008) +#define PCB_PINSEL1_P017_MAT12 ((unsigned int) 0x0000000c) +#define PCB_PINSEL1_P017_MASK ((unsigned int) 0x0000000c) + +#define PCB_PINSEL1_P018_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P018_CAP13 ((unsigned int) 0x00000010) +#define PCB_PINSEL1_P018_MISO1 ((unsigned int) 0x00000020) +#define PCB_PINSEL1_P018_MAT13 ((unsigned int) 0x00000030) +#define PCB_PINSEL1_P018_MASK ((unsigned int) 0x00000030) + +#define PCB_PINSEL1_P019_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P019_MAT12 ((unsigned int) 0x00000040) +#define PCB_PINSEL1_P019_MOSI1 ((unsigned int) 0x00000080) +#define PCB_PINSEL1_P019_CAP12 ((unsigned int) 0x000000c0) + +#define PCB_PINSEL1_P020_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P020_MAT13 ((unsigned int) 0x00000100) +#define PCB_PINSEL1_P020_SSEL1 ((unsigned int) 0x00000200) +#define PCB_PINSEL1_P020_EINT3 ((unsigned int) 0x00000300) +#define PCB_PINSEL1_P020_MASK ((unsigned int) 0x00000300) + +#define PCB_PINSEL1_P021_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P021_PWM5 ((unsigned int) 0x00000400) +#define PCB_PINSEL1_P021_AD16 ((unsigned int) 0x00000800) +#define PCB_PINSEL1_P021_CAP13 ((unsigned int) 0x00000c00) +#define PCB_PINSEL1_P021_MASK ((unsigned int) 0x00000c00) + +#define PCB_PINSEL1_P022_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P022_AD17 ((unsigned int) 0x00001000) +#define PCB_PINSEL1_P022_CAP00 ((unsigned int) 0x00002000) +#define PCB_PINSEL1_P022_MAT00 ((unsigned int) 0x00003000) +#define PCB_PINSEL1_P022_MASK ((unsigned int) 0x00003000) + +#define PCB_PINSEL1_P023_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P023_VBUS ((unsigned int) 0x00004000) +#define PCB_PINSEL1_P023_RSVD2 ((unsigned int) 0x00008000) +#define PCB_PINSEL1_P023_RSVD3 ((unsigned int) 0x0000c000) +#define PCB_PINSEL1_P023_MASK ((unsigned int) 0x0000c000) + +#define PCB_PINSEL1_P024_RSVD0 ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P024_RSVD1 ((unsigned int) 0x00010000) +#define PCB_PINSEL1_P024_RSVD2 ((unsigned int) 0x00020000) +#define PCB_PINSEL1_P024_RSVD3 ((unsigned int) 0x00030000) +#define PCB_PINSEL1_P024_MASK ((unsigned int) 0x00030000) + +#define PCB_PINSEL1_P025_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P025_AD04 ((unsigned int) 0x00040000) +#define PCB_PINSEL1_P025_AOUT ((unsigned int) 0x00080000) +#define PCB_PINSEL1_P025_RSVD3 ((unsigned int) 0x000c0000) +#define PCB_PINSEL1_P025_MASK ((unsigned int) 0x000c0000) + +#define PCB_PINSEL1_P026_RSVD0 ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P026_RSVD1 ((unsigned int) 0x00100000) +#define PCB_PINSEL1_P026_RSVD2 ((unsigned int) 0x00200000) +#define PCB_PINSEL1_P026_RSVD3 ((unsigned int) 0x00300000) +#define PCB_PINSEL1_P026_MASK ((unsigned int) 0x00300000) + +#define PCB_PINSEL1_P027_RSVD0 ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P027_RSVD1 ((unsigned int) 0x00400000) +#define PCB_PINSEL1_P027_RSVD2 ((unsigned int) 0x00800000) +#define PCB_PINSEL1_P027_RSVD3 ((unsigned int) 0x00c00000) +#define PCB_PINSEL1_P027_MASK ((unsigned int) 0x00c00000) + +#define PCB_PINSEL1_P028_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P028_AD01 ((unsigned int) 0x01000000) +#define PCB_PINSEL1_P028_CAP02 ((unsigned int) 0x02000000) +#define PCB_PINSEL1_P028_MAT02 ((unsigned int) 0x03000000) +#define PCB_PINSEL1_P028_MASK ((unsigned int) 0x03000000) + +#define PCB_PINSEL1_P029_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P029_AD02 ((unsigned int) 0x04000000) +#define PCB_PINSEL1_P029_CAP03 ((unsigned int) 0x08000000) +#define PCB_PINSEL1_P029_MAT03 ((unsigned int) 0x0c000000) +#define PCB_PINSEL1_P029_MASK ((unsigned int) 0x0c000000) + +#define PCB_PINSEL1_P030_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P030_AD03 ((unsigned int) 0x10000000) +#define PCB_PINSEL1_P030_EINT3 ((unsigned int) 0x20000000) +#define PCB_PINSEL1_P030_CAP00 ((unsigned int) 0x30000000) +#define PCB_PINSEL1_P030_MASK ((unsigned int) 0x30000000) + +#define PCB_PINSEL1_P031_GPIO ((unsigned int) 0x00000000) +#define PCB_PINSEL1_P031_UPLED ((unsigned int) 0x40000000) +#define PCB_PINSEL1_P031_CONNECT ((unsigned int) 0x80000000) +#define PCB_PINSEL1_P031_RSVD3 ((unsigned int) 0xc0000000) +#define PCB_PINSEL1_P031_MASK ((unsigned int) 0xc0000000) + + +/*############################################################################## +## GPIO - General Purpose I/O +##############################################################################*/ + +#define GPIO0_IOPIN (*(pREG32 (0xe0028000))) +#define GPIO0_IOSET (*(pREG32 (0xe0028004))) +#define GPIO0_IODIR (*(pREG32 (0xe0028008))) +#define GPIO0_IOCLR (*(pREG32 (0xe002800c))) +#define GPIO1_IOPIN (*(pREG32 (0xe0028010))) +#define GPIO1_IOSET (*(pREG32 (0xe0028014))) +#define GPIO1_IODIR (*(pREG32 (0xe0028018))) +#define GPIO1_IOCLR (*(pREG32 (0xe002801c))) + +#define GPIO_IO_P0 ((unsigned int) 0x00000001) +#define GPIO_IO_P1 ((unsigned int) 0x00000002) +#define GPIO_IO_P2 ((unsigned int) 0x00000004) +#define GPIO_IO_P3 ((unsigned int) 0x00000008) +#define GPIO_IO_P4 ((unsigned int) 0x00000010) +#define GPIO_IO_P5 ((unsigned int) 0x00000020) +#define GPIO_IO_P6 ((unsigned int) 0x00000040) +#define GPIO_IO_P7 ((unsigned int) 0x00000080) +#define GPIO_IO_P8 ((unsigned int) 0x00000100) +#define GPIO_IO_P9 ((unsigned int) 0x00000200) +#define GPIO_IO_P10 ((unsigned int) 0x00000400) +#define GPIO_IO_P11 ((unsigned int) 0x00000800) +#define GPIO_IO_P12 ((unsigned int) 0x00001000) +#define GPIO_IO_P13 ((unsigned int) 0x00002000) +#define GPIO_IO_P14 ((unsigned int) 0x00004000) +#define GPIO_IO_P15 ((unsigned int) 0x00008000) +#define GPIO_IO_P16 ((unsigned int) 0x00010000) +#define GPIO_IO_P17 ((unsigned int) 0x00020000) +#define GPIO_IO_P18 ((unsigned int) 0x00040000) +#define GPIO_IO_P19 ((unsigned int) 0x00080000) +#define GPIO_IO_P20 ((unsigned int) 0x00100000) +#define GPIO_IO_P21 ((unsigned int) 0x00200000) +#define GPIO_IO_P22 ((unsigned int) 0x00400000) +#define GPIO_IO_P23 ((unsigned int) 0x00800000) +#define GPIO_IO_P24 ((unsigned int) 0x01000000) +#define GPIO_IO_P25 ((unsigned int) 0x02000000) +#define GPIO_IO_P26 ((unsigned int) 0x04000000) +#define GPIO_IO_P27 ((unsigned int) 0x08000000) +#define GPIO_IO_P28 ((unsigned int) 0x10000000) +#define GPIO_IO_P29 ((unsigned int) 0x20000000) +#define GPIO_IO_P30 ((unsigned int) 0x40000000) +#define GPIO_IO_P31 ((unsigned int) 0x80000000) +#define GPIO_IO_JTAG ((unsigned int) 0x003e0000) + + +/*############################################################################## +## UART0 / UART1 +##############################################################################*/ + +#define UART0_RBR (*(pREG32 (0xe000c000))) +#define UART0_THR (*(pREG32 (0xe000c000))) +#define UART0_IER (*(pREG32 (0xe000c004))) +#define UART0_IIR (*(pREG32 (0xe000c008))) +#define UART0_FCR (*(pREG32 (0xe000c008))) +#define UART0_LCR (*(pREG32 (0xe000c00c))) +#define UART0_LSR (*(pREG32 (0xe000c014))) +#define UART0_SCR (*(pREG32 (0xe000c01c))) +#define UART0_ACR (*(pREG32 (0xe0000020))) +#define UART0_FDR (*(pREG32 (0xe0000028))) +#define UART0_TER (*(pREG32 (0xe0000030))) +#define UART0_DLL (*(pREG32 (0xe000c000))) +#define UART0_DLM (*(pREG32 (0xe000c004))) + +#define UART1_RBR (*(pREG32 (0xe0010000))) +#define UART1_THR (*(pREG32 (0xe0010000))) +#define UART1_IER (*(pREG32 (0xe0010004))) +#define UART1_IIR (*(pREG32 (0xe0010008))) +#define UART1_FCR (*(pREG32 (0xe0010008))) +#define UART1_LCR (*(pREG32 (0xe001000c))) +#define UART1_LSR (*(pREG32 (0xe0010014))) +#define UART1_SCR (*(pREG32 (0xe001001c))) +#define UART1_ACR (*(pREG32 (0xe0010020))) +#define UART1_FDR (*(pREG32 (0xe0010028))) +#define UART1_TER (*(pREG32 (0xe0010030))) +#define UART1_DLL (*(pREG32 (0xe0010000))) +#define UART1_DLM (*(pREG32 (0xe0010004))) +#define UART1_MCR (*(pREG32 (0xe0010010))) +#define UART1_MSR (*(pREG32 (0xe0010018))) + +#define UART_LCR_DLAB (0x00000080) +#define UART_LCR_NOPAR (0x00000000) +#define UART_LCR_1STOP (0x00000000) +#define UART_LCR_8BITS (0x00000003) +#define UART_IER_EI (0x00000003) +#define UART_FCR_EN (0x00000001) +#define UART_FCR_CLR (0x00000006) + + +/*############################################################################## +## I2C +##############################################################################*/ + +#define I2C0_CONSET (*(pREG32 (0xe001c000))) +#define I2C0_STAT (*(pREG32 (0xe001c004))) +#define I2C0_DAT (*(pREG32 (0xe001c008))) +#define I2C0_ADR (*(pREG32 (0xe001c00c))) +#define I2C0_SCLH (*(pREG32 (0xe001c010))) +#define I2C0_SCLL (*(pREG32 (0xe001c014))) +#define I2C0_CONCLR (*(pREG32 (0xe001c018))) + +#define I2C1_CONSET (*(pREG32 (0xe005c000))) +#define I2C1_STAT (*(pREG32 (0xe005c004))) +#define I2C1_DAT (*(pREG32 (0xe005c008))) +#define I2C1_ADR (*(pREG32 (0xe005c00c))) +#define I2C1_SCLH (*(pREG32 (0xe005c010))) +#define I2C1_SCLL (*(pREG32 (0xe005c014))) +#define I2C1_CONCLR (*(pREG32 (0xe005c018))) + +#define I2C_CONSET_AA (0x00000004) +#define I2C_CONSET_SI (0x00000008) +#define I2C_CONSET_STO (0x00000010) +#define I2C_CONSET_STA (0x00000020) +#define I2C_CONSET_I2EN (0x00000040) +#define I2C_CONSET_MASK (0x0000007c) + +#define I2C_STAT_STATMASK (0x000000f8) +#define I2C_STAT_STATSHIFT (3) + +#define I2C_ADDR_GC (0x00000001) +#define I2C_ADDR_ADDRMASK (0x000000fe) +#define I2C_ADDR_ADDRSHIFT (1) + +#define I2C_CONCLR_AAC (0x00000004) +#define I2C_CONCLR_SIC (0x00000008) +#define I2C_CONCLR_STAC (0x00000020) +#define I2C_CONCLR_I2ENC (0x00000040) +#define I2C_CONCLR_MASK (0x0000006c) + + +/*############################################################################## +## SPI - Serial Peripheral Interface +##############################################################################*/ + +#define SPI_SPCR (*(pREG32 (0xe0020000))) +#define SPI_SPSR (*(pREG32 (0xe0020004))) +#define SPI_SPDR (*(pREG32 (0xe0020008))) +#define SPI_SPCCR (*(pREG32 (0xe002000c))) +#define SPI_SPTCR (*(pREG32 (0xe0020010))) +#define SPI_SPTSR (*(pREG32 (0xe0020014))) +#define SPI_SPTOR (*(pREG32 (0xe0020018))) +#define SPI_SPINT (*(pREG32 (0xe002001c))) + + +/*############################################################################## +## SSP - Synchronous Serial Port +##############################################################################*/ + +#define SSP_CR0 (*(pREG32 (0xe0068000))) +#define SSP_CR1 (*(pREG32 (0xe0068004))) +#define SSP_DR (*(pREG32 (0xe0068008))) +#define SSP_SR (*(pREG32 (0xe006800C))) +#define SSP_CPSR (*(pREG32 (0xe0068010))) +#define SSP_IMSC (*(pREG32 (0xe0068014))) +#define SSP_RIS (*(pREG32 (0xe0068018))) +#define SSP_MIS (*(pREG32 (0xe006801C))) +#define SSP_ICR (*(pREG32 (0xe0068020))) + +#define SSP_FIFO_DEPTH (8) + +#define SSP_CR0_DSS_4 ((unsigned int) 0x00000003) +#define SSP_CR0_DSS_5 ((unsigned int) 0x00000004) +#define SSP_CR0_DSS_6 ((unsigned int) 0x00000005) +#define SSP_CR0_DSS_7 ((unsigned int) 0x00000006) +#define SSP_CR0_DSS_8 ((unsigned int) 0x00000007) +#define SSP_CR0_DSS_9 ((unsigned int) 0x00000008) +#define SSP_CR0_DSS 10 ((unsigned int) 0x00000009) +#define SSP_CR0_DSS_11 ((unsigned int) 0x0000000a) +#define SSP_CR0_DSS_12 ((unsigned int) 0x0000000b) +#define SSP_CR0_DSS_13 ((unsigned int) 0x0000000c) +#define SSP_CR0_DSS_14 ((unsigned int) 0x0000000d) +#define SSP_CR0_DSS_15 ((unsigned int) 0x0000000e) +#define SSP_CR0_DSS_16 ((unsigned int) 0x0000000f) +#define SSP_CR0_FRF_SPI ((unsigned int) 0x00000000) +#define SSP_CR0_FRF_SSI ((unsigned int) 0x00000010) +#define SSP_CR0_FRF_MW ((unsigned int) 0x00000020) +#define SSP_CR0_CPOL ((unsigned int) 0x00000040) +#define SSP_CR0_CPHA ((unsigned int) 0x00000080) +#define SSP_CR0_SCR ((unsigned int) 0x0000ff00) + +#define SSP_CR1_LBM ((unsigned int) 0x00000001) +#define SSP_CR1_SSE ((unsigned int) 0x00000002) +#define SSP_CR1_MS ((unsigned int) 0x00000004) +#define SSP_CR1_SOD ((unsigned int) 0x00000008) + +#define SSP_SR_TFE ((unsigned int) 0x00000001) +#define SSP_SR_TNF ((unsigned int) 0x00000002) +#define SSP_SR_RNE ((unsigned int) 0x00000004) +#define SSP_SR_RFF ((unsigned int) 0x00000008) +#define SSP_SR_BSY ((unsigned int) 0x00000010) + +#define SSP_IMSC_RORIM ((unsigned int) 0x00000001) +#define SSP_IMSC_RTIM ((unsigned int) 0x00000002) +#define SSP_IMSC_RXIM ((unsigned int) 0x00000004) +#define SSP_IMSC_TXIM ((unsigned int) 0x00000008) + +#define SSP_RIS_RORRIS ((unsigned int) 0x00000001) +#define SSP_RIS_RTRIS ((unsigned int) 0x00000002) +#define SSP_RIS_RXRIS ((unsigned int) 0x00000004) +#define SSP_RIS_TXRIS ((unsigned int) 0x00000008) + +#define SSP_MIS_RORMIS ((unsigned int) 0x00000001) +#define SSP_MIS_RTMIS ((unsigned int) 0x00000002) +#define SSP_MIS_RXMIS ((unsigned int) 0x00000004) +#define SSP_MIS_TXMIS ((unsigned int) 0x00000008) + +#define SSP_ICR_RORIC ((unsigned int) 0x00000001) +#define SSP_ICR_RTIC ((unsigned int) 0x00000002) + + +/*############################################################################## +## Timer 0 and Timer 1 +##############################################################################*/ + +#define T0_BASE_ADDR (pREG32 (0xe0004000)) +#define T0_IR (*(pREG32 (0xe0004000))) +#define T0_TCR (*(pREG32 (0xe0004004))) +#define T0_TC (*(pREG32 (0xe0004008))) +#define T0_PR (*(pREG32 (0xe000400c))) +#define T0_PC (*(pREG32 (0xe0004010))) +#define T0_MCR (*(pREG32 (0xe0004014))) +#define T0_MR0 (*(pREG32 (0xe0004018))) +#define T0_MR1 (*(pREG32 (0xe000401c))) +#define T0_MR2 (*(pREG32 (0xe0004020))) +#define T0_MR3 (*(pREG32 (0xe0004024))) +#define T0_CCR (*(pREG32 (0xe0004028))) +#define T0_CR0 (*(pREG32 (0xe000402c))) +#define T0_CR1 (*(pREG32 (0xe0004030))) +#define T0_CR2 (*(pREG32 (0xe0004034))) +#define T0_CR3 (*(pREG32 (0xe0004038))) +#define T0_EMR (*(pREG32 (0xe000403c))) +#define T0_CTCR (*(pREG32 (0xe0004070))) + +#define T1_BASE_ADDR (pREG32 (0xe0008000)) +#define T1_IR (*(pREG32 (0xe0008000))) +#define T1_TCR (*(pREG32 (0xe0008004))) +#define T1_TC (*(pREG32 (0xe0008008))) +#define T1_PR (*(pREG32 (0xe000800c))) +#define T1_PC (*(pREG32 (0xe0008010))) +#define T1_MCR (*(pREG32 (0xe0008014))) +#define T1_MR0 (*(pREG32 (0xe0008018))) +#define T1_MR1 (*(pREG32 (0xe000801c))) +#define T1_MR2 (*(pREG32 (0xe0008020))) +#define T1_MR3 (*(pREG32 (0xe0008024))) +#define T1_CCR (*(pREG32 (0xe0008028))) +#define T1_CR0 (*(pREG32 (0xe000802c))) +#define T1_CR1 (*(pREG32 (0xe0008030))) +#define T1_CR2 (*(pREG32 (0xe0008034))) +#define T1_CR3 (*(pREG32 (0xe0008038))) +#define T1_EMR (*(pREG32 (0xe000803c))) +#define T1_CTCR (*(pREG32 (0xe0008070))) + +#define T_IR_MR0 (0x00000001) +#define T_IR_MR1 (0x00000002) +#define T_IR_MR2 (0x00000004) +#define T_IR_MR3 (0x00000008) +#define T_IR_CR0 (0x00000010) +#define T_IR_CR1 (0x00000020) +#define T_IR_CR2 (0x00000040) +#define T_IR_CR3 (0x00000080) +#define T_IR_MASK (0x000000ff) + +#define T_TCR_CE (0x00000001) +#define T_TCR_CR (0x00000002) + +#define T_CTCR_MODE_PCLK (0x00000000) +#define T_CTCR_MODE_CAPRISE (0x00000001) +#define T_CTCR_MODE_CAPFALL (0x00000002) +#define T_CTCR_MODE_CAPBOTH (0x00000003) +#define T_CTCR_MODE_MASK (0x00000003) +#define T_CTCR_CIS_CAPN0 (0x00000000) +#define T_CTCR_CIS_CAPN1 (0x00000004) +#define T_CTCR_CIS_CAPN2 (0x00000008) +#define T_CTCR_CIS_CAPN3 (0x0000000c) +#define T_CTCR_CIS_MASK (0x0000000c) + +#define T_MCR_MR0I (0x00000001) +#define T_MCR_MR0R (0x00000002) +#define T_MCR_MR0S (0x00000004) +#define T_MCR_MR1I (0x00000008) +#define T_MCR_MR1R (0x00000010) +#define T_MCR_MR1S (0x00000020) +#define T_MCR_MR2I (0x00000040) +#define T_MCR_MR2R (0x00000080) +#define T_MCR_MR2S (0x00000100) +#define T_MCR_MR3I (0x00000200) +#define T_MCR_MR3R (0x00000400) +#define T_MCR_MR3S (0x00000800) + +#define T_CCR_CAP0RE (0x00000001) +#define T_CCR_CAP0FE (0x00000002) +#define T_CCR_CAP0I (0x00000004) +#define T_CCR_CAP1RE (0x00000008) +#define T_CCR_CAP1FE (0x00000010) +#define T_CCR_CAP1I (0x00000020) +#define T_CCR_CAP2RE (0x00000040) +#define T_CCR_CAP2FE (0x00000080) +#define T_CCR_CAP2I (0x00000100) +#define T_CCR_CAP3RE (0x00000200) +#define T_CCR_CAP3FE (0x00000400) +#define T_CCR_CAP3I (0x00000800) + +#define T_EMR_EM0 (0x00000001) +#define T_EMR_EM1 (0x00000002) +#define T_EMR_EM2 (0x00000004) +#define T_EMR_EM3 (0x00000008) +#define T_EMR_EMC0_NONE (0x00000000) +#define T_EMR_EMC0_CLEAR (0x00000010) +#define T_EMR_EMC0_SET (0x00000020) +#define T_EMR_EMC0_TOGGLE (0x00000030) +#define T_EMR_EMC0_MASK (0x00000030) +#define T_EMR_EMC1_NONE (0x00000000) +#define T_EMR_EMC1_CLEAR (0x00000040) +#define T_EMR_EMC1_SET (0x00000080) +#define T_EMR_EMC1_TOGGLE (0x000000c0) +#define T_EMR_EMC1_MASK (0x000000c0) +#define T_EMR_EMC2_NONE (0x00000000) +#define T_EMR_EMC2_CLEAR (0x00000100) +#define T_EMR_EMC2_SET (0x00000200) +#define T_EMR_EMC2_TOGGLE (0x00000300) +#define T_EMR_EMC2_MASK (0x00000300) +#define T_EMR_EMC3_NONE (0x00000000) +#define T_EMR_EMC3_CLEAR (0x00000400) +#define T_EMR_EMC3_SET (0x00000800) +#define T_EMR_EMC3_TOGGLE (0x00000c00) +#define T_EMR_EMC3_MASK (0x00000c00) + + +/*############################################################################## +## ADC +##############################################################################*/ + +#define AD0_CR (*(pREG32 (0xe0034000))) +#define AD0_GDR (*(pREG32 (0xe0034004))) +#define AD0_STAT (*(pREG32 (0xe0034030))) +#define AD0_GSR (*(pREG32 (0xe0034008))) +#define AD0_INTEN (*(pREG32 (0xe003400c))) +#define AD0_DR0 (*(pREG32 (0xe0034010))) +#define AD0_DR1 (*(pREG32 (0xe0034014))) +#define AD0_DR2 (*(pREG32 (0xe0034018))) +#define AD0_DR3 (*(pREG32 (0xe003401c))) +#define AD0_DR4 (*(pREG32 (0xe0034020))) +#define AD0_DR5 (*(pREG32 (0xe0034024))) +#define AD0_DR6 (*(pREG32 (0xe0034028))) +#define AD0_DR7 (*(pREG32 (0xe003402c))) + +#define AD1_CR (*(pREG32 (0xe0064000))) +#define AD1_GDR (*(pREG32 (0xe0064004))) +#define AD1_STAT (*(pREG32 (0xe0064030))) +#define AD1_GSR (*(pREG32 (0xe0034008))) +#define AD1_INTEN (*(pREG32 (0xe006400c))) +#define AD1_DR0 (*(pREG32 (0xe0064010))) +#define AD1_DR1 (*(pREG32 (0xe0064014))) +#define AD1_DR2 (*(pREG32 (0xe0064018))) +#define AD1_DR3 (*(pREG32 (0xe006401c))) +#define AD1_DR4 (*(pREG32 (0xe0064020))) +#define AD1_DR5 (*(pREG32 (0xe0064024))) +#define AD1_DR6 (*(pREG32 (0xe0064028))) +#define AD1_DR7 (*(pREG32 (0xe006402c))) + +#define AD_CR_SEL0 (0x00000001) +#define AD_CR_SEL1 (0x00000002) +#define AD_CR_SEL2 (0x00000004) +#define AD_CR_SEL3 (0x00000008) +#define AD_CR_SEL4 (0x00000010) +#define AD_CR_SEL5 (0x00000020) +#define AD_CR_SEL6 (0x00000040) +#define AD_CR_SEL7 (0x00000080) +#define AD_CR_CLKDIV (0x0000ff00) +#define AD_CR_CLKDIVMASK (0x0000ff00) +#define AD_CR_CLKDIVSHIFT (8) +#define AD_CR_BURST (0x00010000) +#define AD_CR_CLKS10 (0x00000000) +#define AD_CR_CLKS9 (0x00020000) +#define AD_CR_CLKS8 (0x00040000) +#define AD_CR_CLKS7 (0x00060000) +#define AD_CR_CLKS6 (0x00080000) +#define AD_CR_CLKS5 (0x000a0000) +#define AD_CR_CLKS4 (0x000c0000) +#define AD_CR_CLKS3 (0x000e0000) +#define AD_CR_PDN (0x00200000) +#define AD_CR_START_NONE (0x00000000) +#define AD_CR_START_NOW (0x01000000) +#define AD_CR_START_P016 (0x02000000) +#define AD_CR_START_P022 (0x03000000) +#define AD_CR_START_MAT01 (0x04000000) +#define AD_CR_START_MAT03 (0x05000000) +#define AD_CR_START_MAT10 (0x06000000) +#define AD_CR_START_MAT11 (0x07000000) +#define AD_CR_EDGE (0x08000000) +#define AD_CR_MASK (0x0f2fffff) + +#define AD_GDR_RESULT (0x0000ffc0) +#define AD_GDR_CHN (0x07000000) +#define AD_GDR_CHNMASK (0x07000000) +#define AD_GDR_CHNSHIFT (24) +#define AD_GDR_OVERRUN (0x40000000) +#define AD_GDR_DONE (0x80000000) +#define AD_GDR_MASK (0xc700ffc0) + +#define AD_GSR_BURST (0x00010000) +#define AD_GSR_START_NONE (0x00000000) +#define AD_GSR_START_NOW (0x01000000) +#define AD_GSR_START_P016 (0x02000000) +#define AD_GSR_START_P022 (0x03000000) +#define AD_GSR_START_MAT01 (0x04000000) +#define AD_GSR_START_MAT03 (0x05000000) +#define AD_GSR_START_MAT10 (0x06000000) +#define AD_GSR_START_MAT11 (0x07000000) +#define AD_GSR_EDGE (0x08000000) +#define AD_GSR_MASK (0x0f010000) + +#define AD_STAT_RSVD (0x00000001) +#define AD_STAT_DONE0 (0x00000001) +#define AD_STAT_DONE1 (0x00000002) +#define AD_STAT_DONE2 (0x00000004) +#define AD_STAT_DONE3 (0x00000008) +#define AD_STAT_DONE4 (0x00000010) +#define AD_STAT_DONE5 (0x00000020) +#define AD_STAT_DONE6 (0x00000040) +#define AD_STAT_DONE7 (0x00000080) +#define AD_STAT_OVERRUN0 (0x00000100) +#define AD_STAT_OVERRUN1 (0x00000200) +#define AD_STAT_OVERRUN2 (0x00000400) +#define AD_STAT_OVERRUN3 (0x00000800) +#define AD_STAT_OVERRUN4 (0x00001000) +#define AD_STAT_OVERRUN5 (0x00002000) +#define AD_STAT_OVERRUN6 (0x00004000) +#define AD_STAT_OVERRUN7 (0x00008000) +#define AD_STAT_ADINT (0x00010000) +#define AD_STAT_MASK (0x0001ffff) + +#define AD_INTEN_AD0 (0x00000001) +#define AD_INTEN_AD1 (0x00000002) +#define AD_INTEN_AD2 (0x00000004) +#define AD_INTEN_AD3 (0x00000008) +#define AD_INTEN_AD4 (0x00000010) +#define AD_INTEN_AD5 (0x00000020) +#define AD_INTEN_AD6 (0x00000040) +#define AD_INTEN_AD7 (0x00000080) +#define AD_INTEN_DONE (0x00000100) +#define AD_INTEN_MASK (0x000001ff) + +#define AD_DR_RESULT (0x0000ffc0) +#define AD_DR_RESULTMASK (0x0000ffc0) +#define AD_DR_RESULTSHIFT (6) +#define AD_DR_OVERRUN (0x40000000) +#define AD_DR_DONE (0x80000000) +#define AD_DR_MASK (0xc000ffc0) + + +/*############################################################################## +## DAC +##############################################################################*/ + +#define DAC_CR (*(pREG32 (0xe006c000))) + +#define DAC_CR_VALUE (0x0000ffc0) +#define DAC_CR_VALUEMASK (0x0000ffc0) +#define DAC_CR_VALUESHIFT (6) +#define DAC_CR_BIAS (0x00010000) +#define DAC_CR_MASK (0x0001ffc0) + + +/*############################################################################## +## PWM +##############################################################################*/ + +#define PWM_IR (*(pREG32 (0xe0014000))) +#define PWM_TCR (*(pREG32 (0xe0014004))) +#define PWM_TC (*(pREG32 (0xe0014008))) +#define PWM_PR (*(pREG32 (0xe001400c))) +#define PWM_PC (*(pREG32 (0xe0014010))) +#define PWM_MCR (*(pREG32 (0xe0014014))) +#define PWM_MR0 (*(pREG32 (0xe0014018))) +#define PWM_MR1 (*(pREG32 (0xe001401c))) +#define PWM_MR2 (*(pREG32 (0xe0014020))) +#define PWM_MR3 (*(pREG32 (0xe0014024))) +#define PWM_MR4 (*(pREG32 (0xe0014040))) +#define PWM_MR5 (*(pREG32 (0xe0014044))) +#define PWM_MR6 (*(pREG32 (0xe0014048))) +#define PWM_EMR (*(pREG32 (0xe001403c))) +#define PWM_PCR (*(pREG32 (0xe001404c))) +#define PWM_LER (*(pREG32 (0xe0014050))) +#define PWM_CCR (*(pREG32 (0xe0014028))) +#define PWM_CR0 (*(pREG32 (0xe001402c))) +#define PWM_CR1 (*(pREG32 (0xe0014030))) +#define PWM_CR2 (*(pREG32 (0xe0014034))) +#define PWM_CR3 (*(pREG32 (0xe0014038))) + + +/*############################################################################## +## RTC +##############################################################################*/ + +#define RTC_ILR (*(pREG32 (0xe0024000))) +#define RTC_CTC (*(pREG32 (0xe0024004))) +#define RTC_CCR (*(pREG32 (0xe0024008))) +#define RTC_CIIR (*(pREG32 (0xe002400c))) +#define RTC_AMR (*(pREG32 (0xe0024010))) +#define RTC_CTIME0 (*(pREG32 (0xe0024014))) +#define RTC_CTIME1 (*(pREG32 (0xe0024018))) +#define RTC_CTIME2 (*(pREG32 (0xe002401c))) + +#define RTC_SEC (*(pREG32 (0xe0024020))) +#define RTC_MIN (*(pREG32 (0xe0024024))) +#define RTC_HOUR (*(pREG32 (0xe0024028))) +#define RTC_DOM (*(pREG32 (0xe002402c))) +#define RTC_DOW (*(pREG32 (0xe0024030))) +#define RTC_DOY (*(pREG32 (0xe0024034))) +#define RTC_MONTH (*(pREG32 (0xe0024038))) +#define RTC_YEAR (*(pREG32 (0xe002403c))) + +#define RTC_ALSEC (*(pREG32 (0xe0024060))) +#define RTC_ALMIN (*(pREG32 (0xe0024064))) +#define RTC_ALHOUR (*(pREG32 (0xe0024068))) +#define RTC_ALDOM (*(pREG32 (0xe002406c))) +#define RTC_ALDOW (*(pREG32 (0xe0024070))) +#define RTC_ALDOY (*(pREG32 (0xe0024074))) +#define RTC_ALMON (*(pREG32 (0xe0024078))) +#define RTC_ALYEAR (*(pREG32 (0xe002407c))) + +#define RTC_PREINT (*(pREG32 (0xe0024080))) +#define RTC_PREFRAC (*(pREG32 (0xe0024084))) + +#define RTC_ILR_RTCCIF (0x00000001) +#define RTC_ILR_RTCALF (0x00000002) +#define RTC_ILR_MASK (0x00000003) + +#define RTC_CCR_CLKEN (0x00000001) +#define RTC_CCR_CTCRST (0x00000002) +#define RTC_CCR_TEST (0x0000000c) +#define RTC_CCR_CLKSRC (0x00000010) + +#define RTC_CIIR_IMSEC (0x00000001) +#define RTC_CIIR_IMMIN (0x00000002) +#define RTC_CIIR_IMHOUR (0x00000004) +#define RTC_CIIR_IMDOM (0x00000008) +#define RTC_CIIR_IMDOW (0x00000010) +#define RTC_CIIR_IMDOY (0x00000020) +#define RTC_CIIR_IMMON (0x00000040) +#define RTC_CIIR_IMYEAR (0x00000080) +#define RTC_CIIR_IMMASK (0x000000ff) + +#define RTC_AMR_AMRSEC (0x00000001) +#define RTC_AMR_AMRMIN (0x00000002) +#define RTC_AMR_AMRHOUR (0x00000004) +#define RTC_AMR_AMRDOM (0x00000008) +#define RTC_AMR_AMRDOW (0x00000010) +#define RTC_AMR_AMRDOY (0x00000020) +#define RTC_AMR_AMRMON (0x00000040) +#define RTC_AMR_AMRYEAR (0x00000080) +#define RTC_AMR_AMRMASK (0x000000ff) + +typedef struct __attribute__ ((packed)) +{ + union + { + struct + { + unsigned int counter : 14; + unsigned int rsvd15_31 : 18; + }; + + unsigned int i; + }; +} +rtcCTC_t; + +typedef struct __attribute__ ((packed)) +{ + union + { + struct + { + unsigned int seconds : 6; + unsigned int rsvd7_6 : 2; + unsigned int minutes : 6; + unsigned int rsvd14_15 : 2; + unsigned int hours : 5; + unsigned int rsvd21_23 : 3; + unsigned int dow : 3; + unsigned int rsvd27_31 : 5; + }; + + unsigned int i; + }; +} +rtcCTIME0_t; + +typedef struct __attribute__ ((packed)) +{ + union + { + struct + { + unsigned int dom : 5; + unsigned int rsvd5_7 : 3; + unsigned int month : 4; + unsigned int rsvd12_15 : 4; + unsigned int year : 12; + unsigned int rsvd28_31 : 4; + }; + + unsigned int i; + }; +} +rtcCTIME1_t; + +typedef struct __attribute__ ((packed)) +{ + union + { + struct + { + unsigned int doy : 12; + unsigned int rsvd12_31 : 20; + }; + + unsigned int i; + }; +} +rtcCTIME2_t; + + +/*############################################################################## +## WD - Watchdog +##############################################################################*/ + +#define WD_MOD (*(pREG32 (0xe0000000))) +#define WD_TC (*(pREG32 (0xe0000004))) +#define WD_FEED (*(pREG32 (0xe0000008))) +#define WD_TV (*(pREG32 (0xe000000c))) + +#define WD_MOD_WDEN (0x00000001) +#define WD_MOD_RESET (0x00000002) +#define WD_MOD_TOF (0x00000004) +#define WD_MOD_INT (0x00000008) +#define WD_MOD_MASK (0x0000000f) + +#define WD_FEED_FEED1 (0x000000aa) +#define WD_FEED_FEED2 (0x00000055) + + +/*############################################################################## +## System Control Block +##############################################################################*/ + +#define SCB_MEMMAP (*(pREG32 (0xe01fc040))) +#define SCB_PLLCON (*(pREG32 (0xe01fc080))) +#define SCB_PLLCFG (*(pREG32 (0xe01fc084))) +#define SCB_PLLSTAT (*(pREG32 (0xe01fc088))) +#define SCB_PLLFEED (*(pREG32 (0xe01fc08c))) +#define SCB_PCON (*(pREG32 (0xe01fc0c0))) +#define SCB_PCONP (*(pREG32 (0xe01fc0c4))) +#define SCB_VPBDIV (*(pREG32 (0xe01fc100))) +#define SCB_EXTINT (*(pREG32 (0xe01fc140))) +#define SCB_INTWAKE (*(pREG32 (0xe01fc144))) +#define SCB_EXTMODE (*(pREG32 (0xe01fc148))) +#define SCB_EXTPOLAR (*(pREG32 (0xe01fc14c))) +#define SCB_RSIR (*(pREG32 (0xe01fc180))) +#define SCB_CSPR (*(pREG32 (0xe01fc184))) +#define SCB_SCS (*(pREG32 (0xe01fc1a0))) + +#define SCB_MEMMAP_BLM (0x00000000) +#define SCB_MEMMAP_UFL (0x00000001) +#define SCB_MEMMAP_URM (0x00000002) +#define SCB_MEMMAP_RSVD (0x00000003) +#define SCB_MEMMAP_MASK (0x00000003) + +#define SCB_PLLCON_PLLE (0x00000001) +#define SCB_PLLCON_PLLC (0x00000002) +#define SCB_PLLCON_MASK (0x00000003) + +#define SCB_PLLCFG_MSEL (0x0000001f) +#define SCB_PLLCFG_PSEL (0x00000060) +#define SCB_PLLCFG_MUL1 (0x00000000) +#define SCB_PLLCFG_MUL2 (0x00000001) +#define SCB_PLLCFG_MUL3 (0x00000002) +#define SCB_PLLCFG_MUL4 (0x00000003) +#define SCB_PLLCFG_MUL5 (0x00000004) +#define SCB_PLLCFG_MUL6 (0x00000005) +#define SCB_PLLCFG_MUL7 (0x00000006) +#define SCB_PLLCFG_MUL8 (0x00000007) +#define SCB_PLLCFG_MUL9 (0x00000008) +#define SCB_PLLCFG_MUL10 (0x00000009) +#define SCB_PLLCFG_MUL11 (0x0000000a) +#define SCB_PLLCFG_MUL12 (0x0000000b) +#define SCB_PLLCFG_MUL13 (0x0000000c) +#define SCB_PLLCFG_MUL14 (0x0000000d) +#define SCB_PLLCFG_MUL15 (0x0000000e) +#define SCB_PLLCFG_MUL16 (0x0000000f) +#define SCB_PLLCFG_MUL17 (0x00000010) +#define SCB_PLLCFG_MUL18 (0x00000011) +#define SCB_PLLCFG_MUL19 (0x00000012) +#define SCB_PLLCFG_MUL20 (0x00000013) +#define SCB_PLLCFG_MUL21 (0x00000014) +#define SCB_PLLCFG_MUL22 (0x00000015) +#define SCB_PLLCFG_MUL23 (0x00000016) +#define SCB_PLLCFG_MUL24 (0x00000017) +#define SCB_PLLCFG_MUL25 (0x00000018) +#define SCB_PLLCFG_MUL26 (0x00000019) +#define SCB_PLLCFG_MUL27 (0x0000001a) +#define SCB_PLLCFG_MUL28 (0x0000001b) +#define SCB_PLLCFG_MUL29 (0x0000001c) +#define SCB_PLLCFG_MUL30 (0x0000001d) +#define SCB_PLLCFG_MUL31 (0x0000001e) +#define SCB_PLLCFG_MUL32 (0x0000001f) +#define SCB_PLLCFG_DIV1 (0x00000000) +#define SCB_PLLCFG_DIV2 (0x00000020) +#define SCB_PLLCFG_DIV4 (0x00000040) +#define SCB_PLLCFG_DIV8 (0x00000060) +#define SCB_PLLCFG_MASK (0x0000007f) + +#define SCB_PLLSTAT_MSEL (0x0000001f) +#define SCB_PLLSTAT_PSEL (0x00000060) +#define SCB_PLLSTAT_PLLE (0x00000100) +#define SCB_PLLSTAT_PLLC (0x00000200) +#define SCB_PLLSTAT_PLOCK (0x00000400) + +#define SCB_PLLFEED_FEED1 (0x000000aa) +#define SCB_PLLFEED_FEED2 (0x00000055) + +#define SCB_PCON_IDL (0x00000001) +#define SCB_PCON_PD (0x00000002) +#define SCB_PCON_PDBOD (0x00000004) +#define SCB_PCON_BODPDM (0x00000008) +#define SCB_PCON_BOGD (0x00000010) +#define SCB_PCON_BORD (0x00000020) +#define SCB_PCON_MASK (0x0000003f) + +#define SCB_PCONP_PCTIM0 (0x00000002) +#define SCB_PCONP_PCTIM1 (0x00000004) +#define SCB_PCONP_PCUART0 (0x00000008) +#define SCB_PCONP_PCUART1 (0x00000010) +#define SCB_PCONP_PCPWM0 (0x00000020) +#define SCB_PCONP_PCI2C0 (0x00000080) +#define SCB_PCONP_PCSPI0 (0x00000100) +#define SCB_PCONP_PCRTC (0x00000200) +#define SCB_PCONP_PCSPI1 (0x00000400) +#define SCB_PCONP_PCAD0 (0x00001000) +#define SCB_PCONP_PCI2C1 (0x00080000) +#define SCB_PCONP_PCAD1 (0x00100000) +#define SCB_PCONP_PUSB (0x80000000) +#define SCB_PCONP_MASK (0x801817be) + +#define SCB_VPBDIV_25 (0x00000000) +#define SCB_VPBDIV_100 (0x00000001) +#define SCB_VPBDIV_50 (0x00000002) +#define SCB_VPBDIV_RSVD (0x00000003) +#define SCB_VPBDIV_MASK (0x00000003) + +#define SCB_EXTINT_EINT0 (0x00000001) +#define SCB_EXTINT_EINT1 (0x00000002) +#define SCB_EXTINT_EINT2 (0x00000004) +#define SCB_EXTINT_EINT3 (0x00000008) +#define SCB_EXTINT_MASK (0x0000000f) + +#define SCB_INTWAKE_EINT0 (0x00000001) +#define SCB_INTWAKE_EINT1 (0x00000002) +#define SCB_INTWAKE_EINT2 (0x00000004) +#define SCB_INTWAKE_EINT3 (0x00000008) +#define SCB_INTWAKE_USB (0x00000020) +#define SCB_INTWAKE_BOD (0x00004000) +#define SCB_INTWAKE_RTC (0x00008000) +#define SCB_INTWAKE_MASK (0x0000c02f) + +#define SCB_EXTMODE_EINT0 (0x00000001) +#define SCB_EXTMODE_EINT1 (0x00000002) +#define SCB_EXTMODE_EINT2 (0x00000004) +#define SCB_EXTMODE_EINT3 (0x00000008) +#define SCB_EXTMODE_MASK (0x0000000f) + +#define SCB_EXTPOLAR_EINT0 (0x00000001) +#define SCB_EXTPOLAR_EINT1 (0x00000002) +#define SCB_EXTPOLAR_EINT2 (0x00000004) +#define SCB_EXTPOLAR_EINT3 (0x00000008) +#define SCB_EXTPOLAR_MASK (0x0000000f) + +#define SCB_RSIR_POR (0x00000001) +#define SCB_RSIR_EXTR (0x00000002) +#define SCB_RSIR_WDTR (0x00000004) +#define SCB_RSIR_BODR (0x00000008) +#define SCB_RSIR_MASK (0x0000000f) + +#define SCB_SCS_GPIO0M (0x00000001) +#define SCB_SCS_GPIO1M (0x00000002) +#define SCB_SCS_MASK (0x00000003) + + +/*############################################################################## +## System Control Block (USB) +##############################################################################*/ + +#define USB_PLLCON (*(pREG32 (0xe01fc0a0))) +#define USB_PLLCFG (*(pREG32 (0xe01fc0a4))) +#define USB_PLLSTAT (*(pREG32 (0xe01fc0a8))) +#define USB_PLLFEED (*(pREG32 (0xe01fc0ac))) + +#define USB_IntSt (*(pREG32 (0xe01fc1c0))) + +#define USB_DevIntSt (*(pREG32 (0xe0090000))) +#define USB_DevIntEn (*(pREG32 (0xe0090004))) +#define USB_DevIntClr (*(pREG32 (0xe0090008))) +#define USB_DevIntSet (*(pREG32 (0xe009000c))) +#define USB_DevIntPri (*(pREG32 (0xe009002c))) + +#define USB_EpIntSt (*(pREG32 (0xe0090030))) +#define USB_EpIntEn (*(pREG32 (0xe0090034))) +#define USB_EpIntClr (*(pREG32 (0xe0090038))) +#define USB_EpIntSet (*(pREG32 (0xe009003c))) +#define USB_EpIntPri (*(pREG32 (0xe0090040))) + +#define USB_ReEP (*(pREG32 (0xe0090044))) +#define USB_EpInd (*(pREG32 (0xe0090048))) +#define USB_MaxPSize (*(pREG32 (0xe009004c))) + +#define USB_RxData (*(pREG32 (0xe0090018))) +#define USB_RxPLen (*(pREG32 (0xe0090020))) +#define USB_TxData (*(pREG32 (0xe009001c))) +#define USB_TxPLen (*(pREG32 (0xe0090024))) +#define USB_Ctrl (*(pREG32 (0xe0090028))) + +#define USB_CmdCode (*(pREG32 (0xe0090010))) +#define USB_CmdData (*(pREG32 (0xe0090014))) + +#define USB_DMARSt (*(pREG32 (0xe0090050))) +#define USB_DMARClr (*(pREG32 (0xe0090054))) +#define USB_DMARSet (*(pREG32 (0xe0090058))) +#define USB_UDCAH (*(pREG32 (0xe0090080))) +#define USB_EpDMASt (*(pREG32 (0xe0090084))) +#define USB_EpDMAEn (*(pREG32 (0xe0090088))) +#define USB_EpDMADis (*(pREG32 (0xe009008c))) +#define USB_DMAIntSt (*(pREG32 (0xe0090090))) +#define USB_DMAIntEn (*(pREG32 (0xe0090094))) +#define USB_EoTIntSt (*(pREG32 (0xe00900a0))) +#define USB_EoTIntClr (*(pREG32 (0xe00900a4))) +#define USB_EoTIntSet (*(pREG32 (0xe00900a8))) +#define USB_NDDRIntSt (*(pREG32 (0xe00900ac))) +#define USB_NDDRIntClr (*(pREG32 (0xe00900b0))) +#define USB_NDDRIntSet (*(pREG32 (0xe00900b4))) +#define USB_SysErrIntSt (*(pREG32 (0xe00900b8))) +#define USB_SysErrIntClr (*(pREG32 (0xe00900bc))) +#define USB_SysErrIntSet (*(pREG32 (0xe00900c0))) + +#define USB_PLLCON_PLLE (0x00000001) +#define USB_PLLCON_PLLC (0x00000002) +#define USB_PLLCON_MASK (0x00000003) + +#define USB_PLLCFG_MSEL (0x0000001f) +#define USB_PLLCFG_PSEL (0x00000060) +#define USB_PLLCFG_MUL1 (0x00000000) +#define USB_PLLCFG_MUL2 (0x00000001) +#define USB_PLLCFG_MUL3 (0x00000002) +#define USB_PLLCFG_MUL4 (0x00000003) +#define USB_PLLCFG_MUL5 (0x00000004) +#define USB_PLLCFG_MUL6 (0x00000005) +#define USB_PLLCFG_MUL7 (0x00000006) +#define USB_PLLCFG_MUL8 (0x00000007) +#define USB_PLLCFG_MUL9 (0x00000008) +#define USB_PLLCFG_MUL10 (0x00000009) +#define USB_PLLCFG_MUL11 (0x0000000a) +#define USB_PLLCFG_MUL12 (0x0000000b) +#define USB_PLLCFG_MUL13 (0x0000000c) +#define USB_PLLCFG_MUL14 (0x0000000d) +#define USB_PLLCFG_MUL15 (0x0000000e) +#define USB_PLLCFG_MUL16 (0x0000000f) +#define USB_PLLCFG_MUL17 (0x00000010) +#define USB_PLLCFG_MUL18 (0x00000011) +#define USB_PLLCFG_MUL19 (0x00000012) +#define USB_PLLCFG_MUL20 (0x00000013) +#define USB_PLLCFG_MUL21 (0x00000014) +#define USB_PLLCFG_MUL22 (0x00000015) +#define USB_PLLCFG_MUL23 (0x00000016) +#define USB_PLLCFG_MUL24 (0x00000017) +#define USB_PLLCFG_MUL25 (0x00000018) +#define USB_PLLCFG_MUL26 (0x00000019) +#define USB_PLLCFG_MUL27 (0x0000001a) +#define USB_PLLCFG_MUL28 (0x0000001b) +#define USB_PLLCFG_MUL29 (0x0000001c) +#define USB_PLLCFG_MUL30 (0x0000001d) +#define USB_PLLCFG_MUL31 (0x0000001e) +#define USB_PLLCFG_MUL32 (0x0000001f) +#define USB_PLLCFG_DIV1 (0x00000000) +#define USB_PLLCFG_DIV2 (0x00000020) +#define USB_PLLCFG_DIV4 (0x00000040) +#define USB_PLLCFG_DIV8 (0x00000060) +#define USB_PLLCFG_MASK (0x0000007f) + +#define USB_PLLSTAT_MSEL (0x0000001f) +#define USB_PLLSTAT_PSEL (0x00000060) +#define USB_PLLSTAT_PLLE (0x00000100) +#define USB_PLLSTAT_PLLC (0x00000200) +#define USB_PLLSTAT_PLOCK (0x00000400) + +#define USB_PLLFEED_FEED1 (0x000000aa) +#define USB_PLLFEED_FEED2 (0x00000055) + +#define USB_IntSt_REQLP (0x00000001) +#define USB_IntSt_REQHP (0x00000002) +#define USB_IntSt_REQDMA (0x00000004) +#define USB_IntSt_NeedClock (0x00000100) +#define USB_IntSt_EnUSBInts (0x80000000) +#define USB_IntSt_MASK (0x80000107) + +#define USB_DevIntSt_FRAME (0x00000001) +#define USB_DevIntSt_EPFAST (0x00000002) +#define USB_DevIntSt_EPSLOW (0x00000004) +#define USB_DevIntSt_DEVSTAT (0x00000008) +#define USB_DevIntSt_CCEMTY (0x00000010) +#define USB_DevIntSt_CDFULL (0x00000020) +#define USB_DevIntSt_RxENDPKT (0x00000040) +#define USB_DevIntSt_TxENDPKT (0x00000080) +#define USB_DevIntSt_EPRLZED (0x00000100) +#define USB_DevIntSt_EPRINT (0x00000200) +#define USB_DevIntSt_MASK (0x000003ff) + +#define USB_DevIntEn_NONE (0x00000000) +#define USB_DevIntEn_FRAME (0x00000001) +#define USB_DevIntEn_EPFAST (0x00000002) +#define USB_DevIntEn_EPSLOW (0x00000004) +#define USB_DevIntEn_DEVSTAT (0x00000008) +#define USB_DevIntEn_CCEMTY (0x00000010) +#define USB_DevIntEn_CDFULL (0x00000020) +#define USB_DevIntEn_RxENDPKT (0x00000040) +#define USB_DevIntEn_TxENDPKT (0x00000080) +#define USB_DevIntEn_EPRLZED (0x00000100) +#define USB_DevIntEn_EPRINT (0x00000200) +#define USB_DevIntEn_MASK (0x000003ff) + +#define USB_DevIntClr_FRAME (0x00000001) +#define USB_DevIntClr_EPFAST (0x00000002) +#define USB_DevIntClr_EPSLOW (0x00000004) +#define USB_DevIntClr_DEVSTAT (0x00000008) +#define USB_DevIntClr_CCEMTY (0x00000010) +#define USB_DevIntClr_CDFULL (0x00000020) +#define USB_DevIntClr_RxENDPKT (0x00000040) +#define USB_DevIntClr_TxENDPKT (0x00000080) +#define USB_DevIntClr_EPRLZED (0x00000100) +#define USB_DevIntClr_EPRINT (0x00000200) +#define USB_DevIntClr_ALL (0x000003ff) +#define USB_DevIntClr_MASK (0x000003ff) + +#define USB_DevIntSet_FRAME (0x00000001) +#define USB_DevIntSet_EPFAST (0x00000002) +#define USB_DevIntSet_EPSLOW (0x00000004) +#define USB_DevIntSet_DEVSTAT (0x00000008) +#define USB_DevIntSet_CCEMTY (0x00000010) +#define USB_DevIntSet_CDFULL (0x00000020) +#define USB_DevIntSet_RxENDPKT (0x00000040) +#define USB_DevIntSet_TxENDPKT (0x00000080) +#define USB_DevIntSet_EPRLZED (0x00000100) +#define USB_DevIntSet_EPRINT (0x00000200) +#define USB_DevIntSet_MASK (0x000003ff) + +#define USB_DevIntPri_FRAME (0x00000001) +#define USB_DevIntPri_EPFAST (0x00000002) +#define USB_DevIntPri_MASK (0x00000003) + +#define USB_EpIntSt_EP0RX (0x00000001) +#define USB_EpIntSt_EP0TX (0x00000002) +#define USB_EpIntSt_EP1RX (0x00000004) +#define USB_EpIntSt_EP1TX (0x00000008) +#define USB_EpIntSt_EP2RX (0x00000010) +#define USB_EpIntSt_EP2TX (0x00000020) +#define USB_EpIntSt_EP3RX (0x00000040) +#define USB_EpIntSt_EP3TX (0x00000080) +#define USB_EpIntSt_EP4RX (0x00000100) +#define USB_EpIntSt_EP4TX (0x00000200) +#define USB_EpIntSt_EP5RX (0x00000400) +#define USB_EpIntSt_EP5TX (0x00000800) +#define USB_EpIntSt_EP6RX (0x00001000) +#define USB_EpIntSt_EP6TX (0x00002000) +#define USB_EpIntSt_EP7RX (0x00004000) +#define USB_EpIntSt_EP7TX (0x00008000) +#define USB_EpIntSt_EP8RX (0x00010000) +#define USB_EpIntSt_EP8TX (0x00020000) +#define USB_EpIntSt_EP9RX (0x00040000) +#define USB_EpIntSt_EP9TX (0x00080000) +#define USB_EpIntSt_EP10RX (0x00100000) +#define USB_EpIntSt_EP10TX (0x00200000) +#define USB_EpIntSt_EP11RX (0x00400000) +#define USB_EpIntSt_EP11TX (0x00800000) +#define USB_EpIntSt_EP12RX (0x01000000) +#define USB_EpIntSt_EP12TX (0x02000000) +#define USB_EpIntSt_EP13RX (0x04000000) +#define USB_EpIntSt_EP13TX (0x08000000) +#define USB_EpIntSt_EP14RX (0x10000000) +#define USB_EpIntSt_EP14TX (0x20000000) +#define USB_EpIntSt_EP15RX (0x40000000) +#define USB_EpIntSt_EP15TX (0x80000000) +#define USB_EpIntSt_MASK (0xffffffff) + +#define USB_EpIntEn_NONE (0x00000000) +#define USB_EpIntEn_EP0RX (0x00000001) +#define USB_EpIntEn_EP0TX (0x00000002) +#define USB_EpIntEn_EP1RX (0x00000004) +#define USB_EpIntEn_EP1TX (0x00000008) +#define USB_EpIntEn_EP2RX (0x00000010) +#define USB_EpIntEn_EP2TX (0x00000020) +#define USB_EpIntEn_EP3RX (0x00000040) +#define USB_EpIntEn_EP3TX (0x00000080) +#define USB_EpIntEn_EP4RX (0x00000100) +#define USB_EpIntEn_EP4TX (0x00000200) +#define USB_EpIntEn_EP5RX (0x00000400) +#define USB_EpIntEn_EP5TX (0x00000800) +#define USB_EpIntEn_EP6RX (0x00001000) +#define USB_EpIntEn_EP6TX (0x00002000) +#define USB_EpIntEn_EP7RX (0x00004000) +#define USB_EpIntEn_EP7TX (0x00008000) +#define USB_EpIntEn_EP8RX (0x00010000) +#define USB_EpIntEn_EP8TX (0x00020000) +#define USB_EpIntEn_EP9RX (0x00040000) +#define USB_EpIntEn_EP9TX (0x00080000) +#define USB_EpIntEn_EP10RX (0x00100000) +#define USB_EpIntEn_EP10TX (0x00200000) +#define USB_EpIntEn_EP11RX (0x00400000) +#define USB_EpIntEn_EP11TX (0x00800000) +#define USB_EpIntEn_EP12RX (0x01000000) +#define USB_EpIntEn_EP12TX (0x02000000) +#define USB_EpIntEn_EP13RX (0x04000000) +#define USB_EpIntEn_EP13TX (0x08000000) +#define USB_EpIntEn_EP14RX (0x10000000) +#define USB_EpIntEn_EP14TX (0x20000000) +#define USB_EpIntEn_EP15RX (0x40000000) +#define USB_EpIntEn_EP15TX (0x80000000) +#define USB_EpIntEn_MASK (0xffffffff) + +#define USB_EpIntClr_EP0RX (0x00000001) +#define USB_EpIntClr_EP0TX (0x00000002) +#define USB_EpIntClr_EP1RX (0x00000004) +#define USB_EpIntClr_EP1TX (0x00000008) +#define USB_EpIntClr_EP2RX (0x00000010) +#define USB_EpIntClr_EP2TX (0x00000020) +#define USB_EpIntClr_EP3RX (0x00000040) +#define USB_EpIntClr_EP3TX (0x00000080) +#define USB_EpIntClr_EP4RX (0x00000100) +#define USB_EpIntClr_EP4TX (0x00000200) +#define USB_EpIntClr_EP5RX (0x00000400) +#define USB_EpIntClr_EP5TX (0x00000800) +#define USB_EpIntClr_EP6RX (0x00001000) +#define USB_EpIntClr_EP6TX (0x00002000) +#define USB_EpIntClr_EP7RX (0x00004000) +#define USB_EpIntClr_EP7TX (0x00008000) +#define USB_EpIntClr_EP8RX (0x00010000) +#define USB_EpIntClr_EP8TX (0x00020000) +#define USB_EpIntClr_EP9RX (0x00040000) +#define USB_EpIntClr_EP9TX (0x00080000) +#define USB_EpIntClr_EP10RX (0x00100000) +#define USB_EpIntClr_EP10TX (0x00200000) +#define USB_EpIntClr_EP11RX (0x00400000) +#define USB_EpIntClr_EP11TX (0x00800000) +#define USB_EpIntClr_EP12RX (0x01000000) +#define USB_EpIntClr_EP12TX (0x02000000) +#define USB_EpIntClr_EP13RX (0x04000000) +#define USB_EpIntClr_EP13TX (0x08000000) +#define USB_EpIntClr_EP14RX (0x10000000) +#define USB_EpIntClr_EP14TX (0x20000000) +#define USB_EpIntClr_EP15RX (0x40000000) +#define USB_EpIntClr_EP15TX (0x80000000) +#define USB_EpIntClr_ALL (0xffffffff) +#define USB_EpIntClr_MASK (0xffffffff) + +#define USB_EpIntSet_EP0RX (0x00000001) +#define USB_EpIntSet_EP0TX (0x00000002) +#define USB_EpIntSet_EP1RX (0x00000004) +#define USB_EpIntSet_EP1TX (0x00000008) +#define USB_EpIntSet_EP2RX (0x00000010) +#define USB_EpIntSet_EP2TX (0x00000020) +#define USB_EpIntSet_EP3RX (0x00000040) +#define USB_EpIntSet_EP3TX (0x00000080) +#define USB_EpIntSet_EP4RX (0x00000100) +#define USB_EpIntSet_EP4TX (0x00000200) +#define USB_EpIntSet_EP5RX (0x00000400) +#define USB_EpIntSet_EP5TX (0x00000800) +#define USB_EpIntSet_EP6RX (0x00001000) +#define USB_EpIntSet_EP6TX (0x00002000) +#define USB_EpIntSet_EP7RX (0x00004000) +#define USB_EpIntSet_EP7TX (0x00008000) +#define USB_EpIntSet_EP8RX (0x00010000) +#define USB_EpIntSet_EP8TX (0x00020000) +#define USB_EpIntSet_EP9RX (0x00040000) +#define USB_EpIntSet_EP9TX (0x00080000) +#define USB_EpIntSet_EP10RX (0x00100000) +#define USB_EpIntSet_EP10TX (0x00200000) +#define USB_EpIntSet_EP11RX (0x00400000) +#define USB_EpIntSet_EP11TX (0x00800000) +#define USB_EpIntSet_EP12RX (0x01000000) +#define USB_EpIntSet_EP12TX (0x02000000) +#define USB_EpIntSet_EP13RX (0x04000000) +#define USB_EpIntSet_EP13TX (0x08000000) +#define USB_EpIntSet_EP14RX (0x10000000) +#define USB_EpIntSet_EP14TX (0x20000000) +#define USB_EpIntSet_EP15RX (0x40000000) +#define USB_EpIntSet_EP15TX (0x80000000) +#define USB_EpIntSet_MASK (0xffffffff) + +#define USB_EpIntPri_EP0RX (0x00000001) +#define USB_EpIntPri_EP0TX (0x00000002) +#define USB_EpIntPri_EP1RX (0x00000004) +#define USB_EpIntPri_EP1TX (0x00000008) +#define USB_EpIntPri_EP2RX (0x00000010) +#define USB_EpIntPri_EP2TX (0x00000020) +#define USB_EpIntPri_EP3RX (0x00000040) +#define USB_EpIntPri_EP3TX (0x00000080) +#define USB_EpIntPri_EP4RX (0x00000100) +#define USB_EpIntPri_EP4TX (0x00000200) +#define USB_EpIntPri_EP5RX (0x00000400) +#define USB_EpIntPri_EP5TX (0x00000800) +#define USB_EpIntPri_EP6RX (0x00001000) +#define USB_EpIntPri_EP6TX (0x00002000) +#define USB_EpIntPri_EP7RX (0x00004000) +#define USB_EpIntPri_EP7TX (0x00008000) +#define USB_EpIntPri_EP8RX (0x00010000) +#define USB_EpIntPri_EP8TX (0x00020000) +#define USB_EpIntPri_EP9RX (0x00040000) +#define USB_EpIntPri_EP9TX (0x00080000) +#define USB_EpIntPri_EP10RX (0x00100000) +#define USB_EpIntPri_EP10TX (0x00200000) +#define USB_EpIntPri_EP11RX (0x00400000) +#define USB_EpIntPri_EP11TX (0x00800000) +#define USB_EpIntPri_EP12RX (0x01000000) +#define USB_EpIntPri_EP12TX (0x02000000) +#define USB_EpIntPri_EP13RX (0x04000000) +#define USB_EpIntPri_EP13TX (0x08000000) +#define USB_EpIntPri_EP14RX (0x10000000) +#define USB_EpIntPri_EP14TX (0x20000000) +#define USB_EpIntPri_EP15RX (0x40000000) +#define USB_EpIntPri_EP15TX (0x80000000) +#define USB_EpIntPri_MASK (0xffffffff) + +#define USB_EpRE_EP0 (0x00000001) +#define USB_EpRE_EP1 (0x00000002) +#define USB_EpRE_EP2 (0x00000004) +#define USB_EpRE_EP3 (0x00000008) +#define USB_EpRE_EP4 (0x00000010) +#define USB_EpRE_EP5 (0x00000020) +#define USB_EpRE_EP6 (0x00000040) +#define USB_EpRE_EP7 (0x00000080) +#define USB_EpRE_EP8 (0x00000100) +#define USB_EpRE_EP9 (0x00000200) +#define USB_EpRE_EP10 (0x00000400) +#define USB_EpRE_EP11 (0x00000800) +#define USB_EpRE_EP12 (0x00001000) +#define USB_EpRE_EP13 (0x00002000) +#define USB_EpRE_EP14 (0x00004000) +#define USB_EpRE_EP15 (0x00008000) +#define USB_EpRE_EP16 (0x00010000) +#define USB_EpRE_EP17 (0x00020000) +#define USB_EpRE_EP18 (0x00040000) +#define USB_EpRE_EP19 (0x00080000) +#define USB_EpRE_EP20 (0x00100000) +#define USB_EpRE_EP21 (0x00200000) +#define USB_EpRE_EP22 (0x00400000) +#define USB_EpRE_EP23 (0x00800000) +#define USB_EpRE_EP24 (0x01000000) +#define USB_EpRE_EP25 (0x02000000) +#define USB_EpRE_EP26 (0x04000000) +#define USB_EpRE_EP27 (0x08000000) +#define USB_EpRE_EP28 (0x10000000) +#define USB_EpRE_EP29 (0x20000000) +#define USB_EpRE_EP30 (0x40000000) +#define USB_EpRE_EP31 (0x80000000) +#define USB_EpRE_MASK (0xffffffff) + +#define USB_EpIn_MASK (0x0000001f) + +#define USB_MaxPSize_MASK (0x000003ff) + +#define USB_RxPLen_PKTLENGTH (0x000003ff) +#define USB_RxPLen_PKTLENGTH_MASK (0x000003ff) +#define USB_RxPLen_DV (0x00000400) +#define USB_RxPLen_PKTRDY (0x00000800) +#define USB_RxPLen_MASK (0x00000fff) + +#define USB_TxPLen_PKTLENGTH (0x000003ff) +#define USB_TxPLen_MASK (0x000003ff) + +#define USB_Ctrl_RDEN (0x00000001) +#define USB_Ctrl_WREN (0x00000002) +#define USB_Ctrl_LOGENDPOINT (0x0000003c) +#define USB_Ctrl_MASK (0x0000003f) + +#define USB_CmdCode_CMDPHASE (0x0000ff00) +#define USB_CmdCode_CMDCODE (0x00ff0000) +#define USB_CmdCode_MASK (0x00ffff00) + +#define USB_CmdData_MASK (0x000000ff) + +#define USB_DMARSt_EP0 (0x00000001) +#define USB_DMARSt_EP1 (0x00000002) +#define USB_DMARSt_EP2 (0x00000004) +#define USB_DMARSt_EP3 (0x00000008) +#define USB_DMARSt_EP4 (0x00000010) +#define USB_DMARSt_EP5 (0x00000020) +#define USB_DMARSt_EP6 (0x00000040) +#define USB_DMARSt_EP7 (0x00000080) +#define USB_DMARSt_EP8 (0x00000100) +#define USB_DMARSt_EP9 (0x00000200) +#define USB_DMARSt_EP10 (0x00000400) +#define USB_DMARSt_EP11 (0x00000800) +#define USB_DMARSt_EP12 (0x00001000) +#define USB_DMARSt_EP13 (0x00002000) +#define USB_DMARSt_EP14 (0x00004000) +#define USB_DMARSt_EP15 (0x00008000) +#define USB_DMARSt_EP16 (0x00010000) +#define USB_DMARSt_EP17 (0x00020000) +#define USB_DMARSt_EP18 (0x00040000) +#define USB_DMARSt_EP19 (0x00080000) +#define USB_DMARSt_EP20 (0x00100000) +#define USB_DMARSt_EP21 (0x00200000) +#define USB_DMARSt_EP22 (0x00400000) +#define USB_DMARSt_EP23 (0x00800000) +#define USB_DMARSt_EP24 (0x01000000) +#define USB_DMARSt_EP25 (0x02000000) +#define USB_DMARSt_EP26 (0x04000000) +#define USB_DMARSt_EP27 (0x08000000) +#define USB_DMARSt_EP28 (0x10000000) +#define USB_DMARSt_EP29 (0x20000000) +#define USB_DMARSt_EP30 (0x40000000) +#define USB_DMARSt_EP31 (0x80000000) +#define USB_DMARSt_MASK (0xffffffff) + +#define USB_DMARClr_EP0 (0x00000001) +#define USB_DMARClr_EP1 (0x00000002) +#define USB_DMARClr_EP2 (0x00000004) +#define USB_DMARClr_EP3 (0x00000008) +#define USB_DMARClr_EP4 (0x00000010) +#define USB_DMARClr_EP5 (0x00000020) +#define USB_DMARClr_EP6 (0x00000040) +#define USB_DMARClr_EP7 (0x00000080) +#define USB_DMARClr_EP8 (0x00000100) +#define USB_DMARClr_EP9 (0x00000200) +#define USB_DMARClr_EP10 (0x00000400) +#define USB_DMARClr_EP11 (0x00000800) +#define USB_DMARClr_EP12 (0x00001000) +#define USB_DMARClr_EP13 (0x00002000) +#define USB_DMARClr_EP14 (0x00004000) +#define USB_DMARClr_EP15 (0x00008000) +#define USB_DMARClr_EP16 (0x00010000) +#define USB_DMARClr_EP17 (0x00020000) +#define USB_DMARClr_EP18 (0x00040000) +#define USB_DMARClr_EP19 (0x00080000) +#define USB_DMARClr_EP20 (0x00100000) +#define USB_DMARClr_EP21 (0x00200000) +#define USB_DMARClr_EP22 (0x00400000) +#define USB_DMARClr_EP23 (0x00800000) +#define USB_DMARClr_EP24 (0x01000000) +#define USB_DMARClr_EP25 (0x02000000) +#define USB_DMARClr_EP26 (0x04000000) +#define USB_DMARClr_EP27 (0x08000000) +#define USB_DMARClr_EP28 (0x10000000) +#define USB_DMARClr_EP29 (0x20000000) +#define USB_DMARClr_EP30 (0x40000000) +#define USB_DMARClr_EP31 (0x80000000) +#define USB_DMARClr_MASK (0xffffffff) + +#define USB_DMARSet_EP0 (0x00000001) +#define USB_DMARSet_EP1 (0x00000002) +#define USB_DMARSet_EP2 (0x00000004) +#define USB_DMARSet_EP3 (0x00000008) +#define USB_DMARSet_EP4 (0x00000010) +#define USB_DMARSet_EP5 (0x00000020) +#define USB_DMARSet_EP6 (0x00000040) +#define USB_DMARSet_EP7 (0x00000080) +#define USB_DMARSet_EP8 (0x00000100) +#define USB_DMARSet_EP9 (0x00000200) +#define USB_DMARSet_EP10 (0x00000400) +#define USB_DMARSet_EP11 (0x00000800) +#define USB_DMARSet_EP12 (0x00001000) +#define USB_DMARSet_EP13 (0x00002000) +#define USB_DMARSet_EP14 (0x00004000) +#define USB_DMARSet_EP15 (0x00008000) +#define USB_DMARSet_EP16 (0x00010000) +#define USB_DMARSet_EP17 (0x00020000) +#define USB_DMARSet_EP18 (0x00040000) +#define USB_DMARSet_EP19 (0x00080000) +#define USB_DMARSet_EP20 (0x00100000) +#define USB_DMARSet_EP21 (0x00200000) +#define USB_DMARSet_EP22 (0x00400000) +#define USB_DMARSet_EP23 (0x00800000) +#define USB_DMARSet_EP24 (0x01000000) +#define USB_DMARSet_EP25 (0x02000000) +#define USB_DMARSet_EP26 (0x04000000) +#define USB_DMARSet_EP27 (0x08000000) +#define USB_DMARSet_EP28 (0x10000000) +#define USB_DMARSet_EP29 (0x20000000) +#define USB_DMARSet_EP30 (0x40000000) +#define USB_DMARSet_EP31 (0x80000000) +#define USB_DMARSet_MASK (0xffffffff) + +#define USB_EpDMASt_EP0 (0x00000001) +#define USB_EpDMASt_EP1 (0x00000002) +#define USB_EpDMASt_EP2 (0x00000004) +#define USB_EpDMASt_EP3 (0x00000008) +#define USB_EpDMASt_EP4 (0x00000010) +#define USB_EpDMASt_EP5 (0x00000020) +#define USB_EpDMASt_EP6 (0x00000040) +#define USB_EpDMASt_EP7 (0x00000080) +#define USB_EpDMASt_EP8 (0x00000100) +#define USB_EpDMASt_EP9 (0x00000200) +#define USB_EpDMASt_EP10 (0x00000400) +#define USB_EpDMASt_EP11 (0x00000800) +#define USB_EpDMASt_EP12 (0x00001000) +#define USB_EpDMASt_EP13 (0x00002000) +#define USB_EpDMASt_EP14 (0x00004000) +#define USB_EpDMASt_EP15 (0x00008000) +#define USB_EpDMASt_EP16 (0x00010000) +#define USB_EpDMASt_EP17 (0x00020000) +#define USB_EpDMASt_EP18 (0x00040000) +#define USB_EpDMASt_EP19 (0x00080000) +#define USB_EpDMASt_EP20 (0x00100000) +#define USB_EpDMASt_EP21 (0x00200000) +#define USB_EpDMASt_EP22 (0x00400000) +#define USB_EpDMASt_EP23 (0x00800000) +#define USB_EpDMASt_EP24 (0x01000000) +#define USB_EpDMASt_EP25 (0x02000000) +#define USB_EpDMASt_EP26 (0x04000000) +#define USB_EpDMASt_EP27 (0x08000000) +#define USB_EpDMASt_EP28 (0x10000000) +#define USB_EpDMASt_EP29 (0x20000000) +#define USB_EpDMASt_EP30 (0x40000000) +#define USB_EpDMASt_EP31 (0x80000000) +#define USB_EpDMASt_MASK (0xffffffff) + +#define USB_EpDMAEn_EP0 (0x00000001) +#define USB_EpDMAEn_EP1 (0x00000002) +#define USB_EpDMAEn_EP2 (0x00000004) +#define USB_EpDMAEn_EP3 (0x00000008) +#define USB_EpDMAEn_EP4 (0x00000010) +#define USB_EpDMAEn_EP5 (0x00000020) +#define USB_EpDMAEn_EP6 (0x00000040) +#define USB_EpDMAEn_EP7 (0x00000080) +#define USB_EpDMAEn_EP8 (0x00000100) +#define USB_EpDMAEn_EP9 (0x00000200) +#define USB_EpDMAEn_EP10 (0x00000400) +#define USB_EpDMAEn_EP11 (0x00000800) +#define USB_EpDMAEn_EP12 (0x00001000) +#define USB_EpDMAEn_EP13 (0x00002000) +#define USB_EpDMAEn_EP14 (0x00004000) +#define USB_EpDMAEn_EP15 (0x00008000) +#define USB_EpDMAEn_EP16 (0x00010000) +#define USB_EpDMAEn_EP17 (0x00020000) +#define USB_EpDMAEn_EP18 (0x00040000) +#define USB_EpDMAEn_EP19 (0x00080000) +#define USB_EpDMAEn_EP20 (0x00100000) +#define USB_EpDMAEn_EP21 (0x00200000) +#define USB_EpDMAEn_EP22 (0x00400000) +#define USB_EpDMAEn_EP23 (0x00800000) +#define USB_EpDMAEn_EP24 (0x01000000) +#define USB_EpDMAEn_EP25 (0x02000000) +#define USB_EpDMAEn_EP26 (0x04000000) +#define USB_EpDMAEn_EP27 (0x08000000) +#define USB_EpDMAEn_EP28 (0x10000000) +#define USB_EpDMAEn_EP29 (0x20000000) +#define USB_EpDMAEn_EP30 (0x40000000) +#define USB_EpDMAEn_EP31 (0x80000000) +#define USB_EpDMAEn_MASK (0xffffffff) + +#define USB_EpDMADis_EP0 (0x00000001) +#define USB_EpDMADis_EP1 (0x00000002) +#define USB_EpDMADis_EP2 (0x00000004) +#define USB_EpDMADis_EP3 (0x00000008) +#define USB_EpDMADis_EP4 (0x00000010) +#define USB_EpDMADis_EP5 (0x00000020) +#define USB_EpDMADis_EP6 (0x00000040) +#define USB_EpDMADis_EP7 (0x00000080) +#define USB_EpDMADis_EP8 (0x00000100) +#define USB_EpDMADis_EP9 (0x00000200) +#define USB_EpDMADis_EP10 (0x00000400) +#define USB_EpDMADis_EP11 (0x00000800) +#define USB_EpDMADis_EP12 (0x00001000) +#define USB_EpDMADis_EP13 (0x00002000) +#define USB_EpDMADis_EP14 (0x00004000) +#define USB_EpDMADis_EP15 (0x00008000) +#define USB_EpDMADis_EP16 (0x00010000) +#define USB_EpDMADis_EP17 (0x00020000) +#define USB_EpDMADis_EP18 (0x00040000) +#define USB_EpDMADis_EP19 (0x00080000) +#define USB_EpDMADis_EP20 (0x00100000) +#define USB_EpDMADis_EP21 (0x00200000) +#define USB_EpDMADis_EP22 (0x00400000) +#define USB_EpDMADis_EP23 (0x00800000) +#define USB_EpDMADis_EP24 (0x01000000) +#define USB_EpDMADis_EP25 (0x02000000) +#define USB_EpDMADis_EP26 (0x04000000) +#define USB_EpDMADis_EP27 (0x08000000) +#define USB_EpDMADis_EP28 (0x10000000) +#define USB_EpDMADis_EP29 (0x20000000) +#define USB_EpDMADis_EP30 (0x40000000) +#define USB_EpDMADis_EP31 (0x80000000) +#define USB_EpDMADis_MASK (0xffffffff) + +#define USB_DMAInstSt_EOT (0x00000001) +#define USB_DMAInstSt_NDDR (0x00000002) +#define USB_DMAInstSt_SE (0x00000004) +#define USB_DMAInstSt_MASK (0x00000007) + +#define USB_DMAInstEn_EOT (0x00000001) +#define USB_DMAInstEn_NDDR (0x00000002) +#define USB_DMAInstEn_SE (0x00000004) +#define USB_DMAInstEn_MASK (0x00000007) + +#define USB_EoTIntSt_EP0 (0x00000001) +#define USB_EoTIntSt_EP1 (0x00000002) +#define USB_EoTIntSt_EP2 (0x00000004) +#define USB_EoTIntSt_EP3 (0x00000008) +#define USB_EoTIntSt_EP4 (0x00000010) +#define USB_EoTIntSt_EP5 (0x00000020) +#define USB_EoTIntSt_EP6 (0x00000040) +#define USB_EoTIntSt_EP7 (0x00000080) +#define USB_EoTIntSt_EP8 (0x00000100) +#define USB_EoTIntSt_EP9 (0x00000200) +#define USB_EoTIntSt_EP10 (0x00000400) +#define USB_EoTIntSt_EP11 (0x00000800) +#define USB_EoTIntSt_EP12 (0x00001000) +#define USB_EoTIntSt_EP13 (0x00002000) +#define USB_EoTIntSt_EP14 (0x00004000) +#define USB_EoTIntSt_EP15 (0x00008000) +#define USB_EoTIntSt_EP16 (0x00010000) +#define USB_EoTIntSt_EP17 (0x00020000) +#define USB_EoTIntSt_EP18 (0x00040000) +#define USB_EoTIntSt_EP19 (0x00080000) +#define USB_EoTIntSt_EP20 (0x00100000) +#define USB_EoTIntSt_EP21 (0x00200000) +#define USB_EoTIntSt_EP22 (0x00400000) +#define USB_EoTIntSt_EP23 (0x00800000) +#define USB_EoTIntSt_EP24 (0x01000000) +#define USB_EoTIntSt_EP25 (0x02000000) +#define USB_EoTIntSt_EP26 (0x04000000) +#define USB_EoTIntSt_EP27 (0x08000000) +#define USB_EoTIntSt_EP28 (0x10000000) +#define USB_EoTIntSt_EP29 (0x20000000) +#define USB_EoTIntSt_EP30 (0x40000000) +#define USB_EoTIntSt_EP31 (0x80000000) +#define USB_EoTIntSt_MASK (0xffffffff) + +#define USB_EoTIntClr_EP0 (0x00000001) +#define USB_EoTIntClr_EP1 (0x00000002) +#define USB_EoTIntClr_EP2 (0x00000004) +#define USB_EoTIntClr_EP3 (0x00000008) +#define USB_EoTIntClr_EP4 (0x00000010) +#define USB_EoTIntClr_EP5 (0x00000020) +#define USB_EoTIntClr_EP6 (0x00000040) +#define USB_EoTIntClr_EP7 (0x00000080) +#define USB_EoTIntClr_EP8 (0x00000100) +#define USB_EoTIntClr_EP9 (0x00000200) +#define USB_EoTIntClr_EP10 (0x00000400) +#define USB_EoTIntClr_EP11 (0x00000800) +#define USB_EoTIntClr_EP12 (0x00001000) +#define USB_EoTIntClr_EP13 (0x00002000) +#define USB_EoTIntClr_EP14 (0x00004000) +#define USB_EoTIntClr_EP15 (0x00008000) +#define USB_EoTIntClr_EP16 (0x00010000) +#define USB_EoTIntClr_EP17 (0x00020000) +#define USB_EoTIntClr_EP18 (0x00040000) +#define USB_EoTIntClr_EP19 (0x00080000) +#define USB_EoTIntClr_EP20 (0x00100000) +#define USB_EoTIntClr_EP21 (0x00200000) +#define USB_EoTIntClr_EP22 (0x00400000) +#define USB_EoTIntClr_EP23 (0x00800000) +#define USB_EoTIntClr_EP24 (0x01000000) +#define USB_EoTIntClr_EP25 (0x02000000) +#define USB_EoTIntClr_EP26 (0x04000000) +#define USB_EoTIntClr_EP27 (0x08000000) +#define USB_EoTIntClr_EP28 (0x10000000) +#define USB_EoTIntClr_EP29 (0x20000000) +#define USB_EoTIntClr_EP30 (0x40000000) +#define USB_EoTIntClr_EP31 (0x80000000) +#define USB_EoTIntClr_MASK (0xffffffff) + +#define USB_EoTIntSet_EP0 (0x00000001) +#define USB_EoTIntSet_EP1 (0x00000002) +#define USB_EoTIntSet_EP2 (0x00000004) +#define USB_EoTIntSet_EP3 (0x00000008) +#define USB_EoTIntSet_EP4 (0x00000010) +#define USB_EoTIntSet_EP5 (0x00000020) +#define USB_EoTIntSet_EP6 (0x00000040) +#define USB_EoTIntSet_EP7 (0x00000080) +#define USB_EoTIntSet_EP8 (0x00000100) +#define USB_EoTIntSet_EP9 (0x00000200) +#define USB_EoTIntSet_EP10 (0x00000400) +#define USB_EoTIntSet_EP11 (0x00000800) +#define USB_EoTIntSet_EP12 (0x00001000) +#define USB_EoTIntSet_EP13 (0x00002000) +#define USB_EoTIntSet_EP14 (0x00004000) +#define USB_EoTIntSet_EP15 (0x00008000) +#define USB_EoTIntSet_EP16 (0x00010000) +#define USB_EoTIntSet_EP17 (0x00020000) +#define USB_EoTIntSet_EP18 (0x00040000) +#define USB_EoTIntSet_EP19 (0x00080000) +#define USB_EoTIntSet_EP20 (0x00100000) +#define USB_EoTIntSet_EP21 (0x00200000) +#define USB_EoTIntSet_EP22 (0x00400000) +#define USB_EoTIntSet_EP23 (0x00800000) +#define USB_EoTIntSet_EP24 (0x01000000) +#define USB_EoTIntSet_EP25 (0x02000000) +#define USB_EoTIntSet_EP26 (0x04000000) +#define USB_EoTIntSet_EP27 (0x08000000) +#define USB_EoTIntSet_EP28 (0x10000000) +#define USB_EoTIntSet_EP29 (0x20000000) +#define USB_EoTIntSet_EP30 (0x40000000) +#define USB_EoTIntSet_EP31 (0x80000000) +#define USB_EoTIntSet_MASK (0xffffffff) + +#define USB_NDDRIntSt_EP0 (0x00000001) +#define USB_NDDRIntSt_EP1 (0x00000002) +#define USB_NDDRIntSt_EP2 (0x00000004) +#define USB_NDDRIntSt_EP3 (0x00000008) +#define USB_NDDRIntSt_EP4 (0x00000010) +#define USB_NDDRIntSt_EP5 (0x00000020) +#define USB_NDDRIntSt_EP6 (0x00000040) +#define USB_NDDRIntSt_EP7 (0x00000080) +#define USB_NDDRIntSt_EP8 (0x00000100) +#define USB_NDDRIntSt_EP9 (0x00000200) +#define USB_NDDRIntSt_EP10 (0x00000400) +#define USB_NDDRIntSt_EP11 (0x00000800) +#define USB_NDDRIntSt_EP12 (0x00001000) +#define USB_NDDRIntSt_EP13 (0x00002000) +#define USB_NDDRIntSt_EP14 (0x00004000) +#define USB_NDDRIntSt_EP15 (0x00008000) +#define USB_NDDRIntSt_EP16 (0x00010000) +#define USB_NDDRIntSt_EP17 (0x00020000) +#define USB_NDDRIntSt_EP18 (0x00040000) +#define USB_NDDRIntSt_EP19 (0x00080000) +#define USB_NDDRIntSt_EP20 (0x00100000) +#define USB_NDDRIntSt_EP21 (0x00200000) +#define USB_NDDRIntSt_EP22 (0x00400000) +#define USB_NDDRIntSt_EP23 (0x00800000) +#define USB_NDDRIntSt_EP24 (0x01000000) +#define USB_NDDRIntSt_EP25 (0x02000000) +#define USB_NDDRIntSt_EP26 (0x04000000) +#define USB_NDDRIntSt_EP27 (0x08000000) +#define USB_NDDRIntSt_EP28 (0x10000000) +#define USB_NDDRIntSt_EP29 (0x20000000) +#define USB_NDDRIntSt_EP30 (0x40000000) +#define USB_NDDRIntSt_EP31 (0x80000000) +#define USB_NDDRIntSt_MASK (0xffffffff) + +#define USB_NDDRIntClr_EP0 (0x00000001) +#define USB_NDDRIntClr_EP1 (0x00000002) +#define USB_NDDRIntClr_EP2 (0x00000004) +#define USB_NDDRIntClr_EP3 (0x00000008) +#define USB_NDDRIntClr_EP4 (0x00000010) +#define USB_NDDRIntClr_EP5 (0x00000020) +#define USB_NDDRIntClr_EP6 (0x00000040) +#define USB_NDDRIntClr_EP7 (0x00000080) +#define USB_NDDRIntClr_EP8 (0x00000100) +#define USB_NDDRIntClr_EP9 (0x00000200) +#define USB_NDDRIntClr_EP10 (0x00000400) +#define USB_NDDRIntClr_EP11 (0x00000800) +#define USB_NDDRIntClr_EP12 (0x00001000) +#define USB_NDDRIntClr_EP13 (0x00002000) +#define USB_NDDRIntClr_EP14 (0x00004000) +#define USB_NDDRIntClr_EP15 (0x00008000) +#define USB_NDDRIntClr_EP16 (0x00010000) +#define USB_NDDRIntClr_EP17 (0x00020000) +#define USB_NDDRIntClr_EP18 (0x00040000) +#define USB_NDDRIntClr_EP19 (0x00080000) +#define USB_NDDRIntClr_EP20 (0x00100000) +#define USB_NDDRIntClr_EP21 (0x00200000) +#define USB_NDDRIntClr_EP22 (0x00400000) +#define USB_NDDRIntClr_EP23 (0x00800000) +#define USB_NDDRIntClr_EP24 (0x01000000) +#define USB_NDDRIntClr_EP25 (0x02000000) +#define USB_NDDRIntClr_EP26 (0x04000000) +#define USB_NDDRIntClr_EP27 (0x08000000) +#define USB_NDDRIntClr_EP28 (0x10000000) +#define USB_NDDRIntClr_EP29 (0x20000000) +#define USB_NDDRIntClr_EP30 (0x40000000) +#define USB_NDDRIntClr_EP31 (0x80000000) +#define USB_NDDRIntClr_MASK (0xffffffff) + +#define USB_NDDRIntSet_EP0 (0x00000001) +#define USB_NDDRIntSet_EP1 (0x00000002) +#define USB_NDDRIntSet_EP2 (0x00000004) +#define USB_NDDRIntSet_EP3 (0x00000008) +#define USB_NDDRIntSet_EP4 (0x00000010) +#define USB_NDDRIntSet_EP5 (0x00000020) +#define USB_NDDRIntSet_EP6 (0x00000040) +#define USB_NDDRIntSet_EP7 (0x00000080) +#define USB_NDDRIntSet_EP8 (0x00000100) +#define USB_NDDRIntSet_EP9 (0x00000200) +#define USB_NDDRIntSet_EP10 (0x00000400) +#define USB_NDDRIntSet_EP11 (0x00000800) +#define USB_NDDRIntSet_EP12 (0x00001000) +#define USB_NDDRIntSet_EP13 (0x00002000) +#define USB_NDDRIntSet_EP14 (0x00004000) +#define USB_NDDRIntSet_EP15 (0x00008000) +#define USB_NDDRIntSet_EP16 (0x00010000) +#define USB_NDDRIntSet_EP17 (0x00020000) +#define USB_NDDRIntSet_EP18 (0x00040000) +#define USB_NDDRIntSet_EP19 (0x00080000) +#define USB_NDDRIntSet_EP20 (0x00100000) +#define USB_NDDRIntSet_EP21 (0x00200000) +#define USB_NDDRIntSet_EP22 (0x00400000) +#define USB_NDDRIntSet_EP23 (0x00800000) +#define USB_NDDRIntSet_EP24 (0x01000000) +#define USB_NDDRIntSet_EP25 (0x02000000) +#define USB_NDDRIntSet_EP26 (0x04000000) +#define USB_NDDRIntSet_EP27 (0x08000000) +#define USB_NDDRIntSet_EP28 (0x10000000) +#define USB_NDDRIntSet_EP29 (0x20000000) +#define USB_NDDRIntSet_EP30 (0x40000000) +#define USB_NDDRIntSet_EP31 (0x80000000) +#define USB_NDDRIntSet_MASK (0xffffffff) + +#define USB_SysErrIntSt_EP0 (0x00000001) +#define USB_SysErrIntSt_EP1 (0x00000002) +#define USB_SysErrIntSt_EP2 (0x00000004) +#define USB_SysErrIntSt_EP3 (0x00000008) +#define USB_SysErrIntSt_EP4 (0x00000010) +#define USB_SysErrIntSt_EP5 (0x00000020) +#define USB_SysErrIntSt_EP6 (0x00000040) +#define USB_SysErrIntSt_EP7 (0x00000080) +#define USB_SysErrIntSt_EP8 (0x00000100) +#define USB_SysErrIntSt_EP9 (0x00000200) +#define USB_SysErrIntSt_EP10 (0x00000400) +#define USB_SysErrIntSt_EP11 (0x00000800) +#define USB_SysErrIntSt_EP12 (0x00001000) +#define USB_SysErrIntSt_EP13 (0x00002000) +#define USB_SysErrIntSt_EP14 (0x00004000) +#define USB_SysErrIntSt_EP15 (0x00008000) +#define USB_SysErrIntSt_EP16 (0x00010000) +#define USB_SysErrIntSt_EP17 (0x00020000) +#define USB_SysErrIntSt_EP18 (0x00040000) +#define USB_SysErrIntSt_EP19 (0x00080000) +#define USB_SysErrIntSt_EP20 (0x00100000) +#define USB_SysErrIntSt_EP21 (0x00200000) +#define USB_SysErrIntSt_EP22 (0x00400000) +#define USB_SysErrIntSt_EP23 (0x00800000) +#define USB_SysErrIntSt_EP24 (0x01000000) +#define USB_SysErrIntSt_EP25 (0x02000000) +#define USB_SysErrIntSt_EP26 (0x04000000) +#define USB_SysErrIntSt_EP27 (0x08000000) +#define USB_SysErrIntSt_EP28 (0x10000000) +#define USB_SysErrIntSt_EP29 (0x20000000) +#define USB_SysErrIntSt_EP30 (0x40000000) +#define USB_SysErrIntSt_EP31 (0x80000000) +#define USB_SysErrIntSt_MASK (0xffffffff) + +#define USB_SysErrIntClr_EP0 (0x00000001) +#define USB_SysErrIntClr_EP1 (0x00000002) +#define USB_SysErrIntClr_EP2 (0x00000004) +#define USB_SysErrIntClr_EP3 (0x00000008) +#define USB_SysErrIntClr_EP4 (0x00000010) +#define USB_SysErrIntClr_EP5 (0x00000020) +#define USB_SysErrIntClr_EP6 (0x00000040) +#define USB_SysErrIntClr_EP7 (0x00000080) +#define USB_SysErrIntClr_EP8 (0x00000100) +#define USB_SysErrIntClr_EP9 (0x00000200) +#define USB_SysErrIntClr_EP10 (0x00000400) +#define USB_SysErrIntClr_EP11 (0x00000800) +#define USB_SysErrIntClr_EP12 (0x00001000) +#define USB_SysErrIntClr_EP13 (0x00002000) +#define USB_SysErrIntClr_EP14 (0x00004000) +#define USB_SysErrIntClr_EP15 (0x00008000) +#define USB_SysErrIntClr_EP16 (0x00010000) +#define USB_SysErrIntClr_EP17 (0x00020000) +#define USB_SysErrIntClr_EP18 (0x00040000) +#define USB_SysErrIntClr_EP19 (0x00080000) +#define USB_SysErrIntClr_EP20 (0x00100000) +#define USB_SysErrIntClr_EP21 (0x00200000) +#define USB_SysErrIntClr_EP22 (0x00400000) +#define USB_SysErrIntClr_EP23 (0x00800000) +#define USB_SysErrIntClr_EP24 (0x01000000) +#define USB_SysErrIntClr_EP25 (0x02000000) +#define USB_SysErrIntClr_EP26 (0x04000000) +#define USB_SysErrIntClr_EP27 (0x08000000) +#define USB_SysErrIntClr_EP28 (0x10000000) +#define USB_SysErrIntClr_EP29 (0x20000000) +#define USB_SysErrIntClr_EP30 (0x40000000) +#define USB_SysErrIntClr_EP31 (0x80000000) +#define USB_SysErrIntClr_MASK (0xffffffff) + +#define USB_SysErrIntSet_EP0 (0x00000001) +#define USB_SysErrIntSet_EP1 (0x00000002) +#define USB_SysErrIntSet_EP2 (0x00000004) +#define USB_SysErrIntSet_EP3 (0x00000008) +#define USB_SysErrIntSet_EP4 (0x00000010) +#define USB_SysErrIntSet_EP5 (0x00000020) +#define USB_SysErrIntSet_EP6 (0x00000040) +#define USB_SysErrIntSet_EP7 (0x00000080) +#define USB_SysErrIntSet_EP8 (0x00000100) +#define USB_SysErrIntSet_EP9 (0x00000200) +#define USB_SysErrIntSet_EP10 (0x00000400) +#define USB_SysErrIntSet_EP11 (0x00000800) +#define USB_SysErrIntSet_EP12 (0x00001000) +#define USB_SysErrIntSet_EP13 (0x00002000) +#define USB_SysErrIntSet_EP14 (0x00004000) +#define USB_SysErrIntSet_EP15 (0x00008000) +#define USB_SysErrIntSet_EP16 (0x00010000) +#define USB_SysErrIntSet_EP17 (0x00020000) +#define USB_SysErrIntSet_EP18 (0x00040000) +#define USB_SysErrIntSet_EP19 (0x00080000) +#define USB_SysErrIntSet_EP20 (0x00100000) +#define USB_SysErrIntSet_EP21 (0x00200000) +#define USB_SysErrIntSet_EP22 (0x00400000) +#define USB_SysErrIntSet_EP23 (0x00800000) +#define USB_SysErrIntSet_EP24 (0x01000000) +#define USB_SysErrIntSet_EP25 (0x02000000) +#define USB_SysErrIntSet_EP26 (0x04000000) +#define USB_SysErrIntSet_EP27 (0x08000000) +#define USB_SysErrIntSet_EP28 (0x10000000) +#define USB_SysErrIntSet_EP29 (0x20000000) +#define USB_SysErrIntSet_EP30 (0x40000000) +#define USB_SysErrIntSet_EP31 (0x80000000) +#define USB_SysErrIntSet_MASK (0xffffffff) + + +/*############################################################################## +## Memory Accelerator Module (MAM) +##############################################################################*/ + +#define MAM_CR (*(pREG32 (0xe01fc000))) +#define MAM_TIM (*(pREG32 (0xe01fc004))) + +#define MAM_CR_DISABLE (0x000000000) +#define MAM_CR_PARTIAL (0x000000001) +#define MAM_CR_FULL (0x000000002) +#define MAM_CR_RSVD (0x000000003) +#define MAM_CR_MASK (0x000000003) + +#define MAM_TIM_RSVD (0x000000000) +#define MAM_TIM_1 (0x000000001) +#define MAM_TIM_2 (0x000000002) +#define MAM_TIM_3 (0x000000003) +#define MAM_TIM_4 (0x000000004) +#define MAM_TIM_5 (0x000000005) +#define MAM_TIM_6 (0x000000006) +#define MAM_TIM_7 (0x000000007) +#define MAM_TIM_MASK (0x000000007) + + +/*############################################################################## +## IAP/ISP +##############################################################################*/ + +#define IAP_LOCATION (0x7ffffff1) +#define IAP_CMD_PREPARE (50) +#define IAP_CMD_COPYRAMTOFLASH (51) +#define IAP_CMD_ERASE (52) +#define IAP_CMD_BLANKCHECK (53) +#define IAP_CMD_READPARTID (54) +#define IAP_CMD_READBOOTCODEVER (55) +#define IAP_CMD_COMPARE (56) +#define IAP_CMD_REINVOKEISP (57) + +#define IAP_RESULT_CMD_SUCCESS (0) +#define IAP_RESULT_INVALID_COMMAND (1) +#define IAP_RESULT_SRC_ADDR_ERROR (2) +#define IAP_RESULT_DST_ADDR_ERROR (3) +#define IAP_RESULT_SRC_ADDR_NOT_MAPPED (4) +#define IAP_RESULT_DST_ADDR_NOT_MAPPED (5) +#define IAP_RESULT_COUNT_ERROR (6) +#define IAP_RESULT_INVALID_SECTOR (7) +#define IAP_RESULT_SECTOR_NOT_BLANK (8) +#define IAP_RESULT_SECTOR_NOT_PREPARED (9) +#define IAP_RESULT_COMPARE_ERROR (10) +#define IAP_RESULT_BUSY (11) +#define IAP_RESULT_PARAM_ERROR (12) +#define IAP_RESULT_ADDR_ERROR (13) +#define IAP_RESULT_ADDR_NOT_MAPPED (14) +#define IAP_RESULT_CMD_LOCKED (15) +#define IAP_RESULT_INVALID_CODE (16) +#define IAP_RESULT_INVALID_BAUD_RATE (17) +#define IAP_RESULT_ANVALID_STOP_BIT (18) +#define IAP_RESULT_CRP_ENABLED (19) +#define IAP_RESULT_LAST (19) + +#endif diff --git a/lpc2148-rom.ld b/lpc2148-rom.ld new file mode 100644 index 0000000..ec63759 --- /dev/null +++ b/lpc2148-rom.ld @@ -0,0 +1,70 @@ +MEMORY +{ + flash : ORIGIN = 0, LENGTH = 500K + ram : ORIGIN = 0x40000000, LENGTH = 32K - 32 +} + +__stack_end__ = 0x40000000 + 32K - 36; + +SECTIONS +{ + . = 0; + startup : + { + __start_of_startup__ = .; + *(.startup) + __end_of_startup__ = .; + } >flash + + prog : + { + __start_of_text__ = .; + __start_of_prog__ = .; + *(.text) + __end_of_prog__ = .; + __start_of_rodata__ = .; + *(.rodata) + *(.rodata*) + __end_of_rodata__ = .; + __start_of_glue7__ = .; + *(.glue_7) + *(.glue_7t) + __end_of_glue7__ = .; + } >flash + + __end_of_text__ = .; + + .protected : + { + __protected_beg__ = .; + *(.protected) + __protected_end__ = .; + } >ram + + .data : + { + __data_beg__ = .; + __data_beg_src__ = __end_of_text__; + *(.data) + __data_end__ = .; + } >ram AT>flash + + .bss : + { + __bss_beg__ = .; + *(.bss) + } >ram + + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); +} + . = ALIGN(32 / 8); + _end = .; + _bss_end__ = .; + __bss_end__ = .; + __heap_beg__ = .; + __heap_end__ = .; + __end__ = . ; + PROVIDE (end = .); diff --git a/lpc2148_demo.fms b/lpc2148_demo.fms new file mode 100755 index 0000000..53a41f3 --- /dev/null +++ b/lpc2148_demo.fms @@ -0,0 +1,183 @@ +[main] +interface=None (ISP) +comport=COM 1 +baudrate=9600 +oscfrequency=16.000000 +i2caddr=26 +device=LPC2148 +hexfile=F:\Katana\LPC2148_Demo\lpc2148.hex +blocknum=27 +block0=0 +block1=0 +block2=0 +block3=0 +block4=0 +block5=0 +block6=0 +block7=0 +block8=0 +block9=0 +block10=0 +block11=0 +block12=0 +block13=0 +block14=0 +block15=0 +block16=0 +block17=0 +block18=0 +block19=0 +block20=0 +block21=0 +block22=0 +block23=0 +block24=0 +block25=0 +block26=0 +allflash=0 +neededflash=1 +verify=0 +fill=0 +checksums=0 +securitybit1=0 +securitybit2=0 +securitybit3=0 +clocks=0 +reset=0 +securitybitslist0=0 +securitybitslist1=0 +securitybitslist2=0 +securitybitslist3=0 +securitybitslist4=0 +securitybitslist5=0 +securitybitslist6=0 +securitybitslist7=0 +securitybitslist8=0 +securitybitslist9=0 +securitybitslist10=0 +securitybitslist11=0 +securitybitslist12=0 +securitybitslist13=0 +securitybitslist14=0 +securitybitslist15=0 +securitybitslist16=0 +securitybitslist17=0 +securitybitslist18=0 +securitybitslist19=0 +securitybitslist20=0 +securitybitslist21=0 +securitybitslist22=0 +securitybitslist23=0 +securitybitslist24=0 +securitybitslist25=0 +securitybitslist26=0 +securitybitslist27=0 +securitybitslist28=0 +securitybitslist29=0 +securitybitslist30=0 +securitybitslist31=0 +securitybitslist32=0 +securitybitslist33=0 +securitybitslist34=0 +securitybitslist35=0 +securitybitslist36=0 +securitybitslist37=0 +securitybitslist38=0 +securitybitslist39=0 +securitybitslist40=0 +securitybitslist41=0 +securitybitslist42=0 +securitybitslist43=0 +securitybitslist44=0 +securitybitslist45=0 +securitybitslist46=0 +securitybitslist47=0 +securitybitslist48=0 +securitybitslist49=0 +securitybitslist50=0 +securitybitslist51=0 +securitybitslist52=0 +securitybitslist53=0 +securitybitslist54=0 +securitybitslist55=0 +securitybitslist56=0 +securitybitslist57=0 +securitybitslist58=0 +securitybitslist59=0 +securitybitslist60=0 +securitybitslist61=0 +securitybitslist62=0 +securitybitslist63=0 +securitybitslist64=0 +securitybitslist65=0 +securitybitslist66=0 +securitybitslist67=0 +securitybitslist68=0 +securitybitslist69=0 +securitybitslist70=0 +securitybitslist71=0 +securitybitslist72=0 +securitybitslist73=0 +securitybitslist74=0 +securitybitslist75=0 +securitybitslist76=0 +securitybitslist77=0 +securitybitslist78=0 +securitybitslist79=0 +securitybitslist80=0 +securitybitslist81=0 +securitybitslist82=0 +securitybitslist83=0 +securitybitslist84=0 +securitybitslist85=0 +securitybitslist86=0 +securitybitslist87=0 +securitybitslist88=0 +securitybitslist89=0 +securitybitslist90=0 +securitybitslist91=0 +securitybitslist92=0 +securitybitslist93=0 +securitybitslist94=0 +securitybitslist95=0 +[advanced] +highspeed=0 +highspeedmax=230400 +clocks6=1 +clocks12=0 +halfduplex=0 +hwenable=1 +hwbootexecrts=0 +hwassert=0 +hwassertlpc=0 +hwt1=50 +hwt2=100 +hwbootlpc=1 +hwbootlpcselector=0 +protectisp=1 +playwav=0 +wavfile= +jitmodule= +jitoptions= +jittimeout= +mytimeouts=0 +regulartimeout=4 +longtimeout=60 +disablesignature=0 +[startbootrom] +command=boot +baudrate=9600 +append=noctrl +send=command +[terminal] +comport=1 +baudrate=9600 +modifycomport=0 +dtrrts=0 +usedelaychar=0 +delaychar=35 +chardelay=1000 +newline=1 +[eeprom] +hexfile= +crchexfilesnum=0 diff --git a/main.c b/main.c new file mode 100644 index 0000000..7f668d0 --- /dev/null +++ b/main.c @@ -0,0 +1,87 @@ +// +// Standard includes +// +#include +#include +#include +#include + +// +// Scheduler includes +// +#include "FreeRTOS.h" +#include "task.h" + +// +// Demo application includes +// +#include "main.h" +#include "adc/adc.h" +#include "cpu/cpu.h" +#include "dac/dac.h" +#include "eints/eints.h" +#include "gps/gps.h" +#include "fiq/fiq.h" +#include "i2c/i2c.h" +#include "iap/iap.h" +#include "leds/leds.h" +#include "monitor/monitor.h" +#include "rtc/rtc.h" +#include "sensors/sensors.h" +#include "uart/uart.h" +#include "usbser/usbser.h" +#include "usbmass/usbmass.h" + +// +// +// +#define BAUD_UART0 115200 +#define BAUD_UART1 4800 + +#ifdef CFG_CONSOLE_UART1 +#undef BAUD_UART1 +#define BAUD_UART1 115200 +#endif + +#if defined CFG_CONSOLE_USB && defined CFG_USB_MSC +#error Cannot have USB console and MSC defined at the same time +#endif + +// +// +// +xTaskHandle taskHandles [TASKHANDLE_LAST]; + +// +// +// +int main (void) +{ + cpuSetupHardware (); + uartInit (0, BAUD_UART0, 64); + uartInit (1, BAUD_UART1, 64); +#ifndef CFG_USB_MSC + usbserInit (); +#else + usbmassInit (); +#endif + rtcInit (); + adcInit (); + dacInit (); + i2cInit (); + eintsInit (); + fiqInit (); + iapInit (); + + memset (taskHandles, 0, sizeof (taskHandles)); + + xTaskCreate (vSensorsTask, (const signed portCHAR * const) "Sensors", 512, NULL, (configMAX_PRIORITIES - 2), &taskHandles [TASKHANDLE_SENSORS]); +#ifndef CFG_CONSOLE_UART1 + xTaskCreate (vGPSTask, (const signed portCHAR * const) "GPS", 768, NULL, (tskIDLE_PRIORITY + 1), &taskHandles [TASKHANDLE_GPS]); +#endif + xTaskCreate (vMonitorTask, (const signed portCHAR * const) "Monitor", 1024, NULL, (tskIDLE_PRIORITY + 1), &taskHandles [TASKHANDLE_MONITOR]); + xTaskCreate (vLEDFlashTask, (const signed portCHAR * const) "LEDx", configMINIMAL_STACK_SIZE, NULL, (tskIDLE_PRIORITY + 1), &taskHandles [TASKHANDLE_LED]); + vTaskStartScheduler (); + + return 0; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..c509d15 --- /dev/null +++ b/main.h @@ -0,0 +1,24 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ + +// +// +// +#define __VERSION "1.20" + +// +// +// +typedef enum +{ + TASKHANDLE_GPS = 0, + TASKHANDLE_SENSORS, + TASKHANDLE_MONITOR, + TASKHANDLE_LED, + TASKHANDLE_LAST +} +taskHandle_e; + +extern xTaskHandle taskHandles [TASKHANDLE_LAST]; + +#endif diff --git a/monitor/Makefile b/monitor/Makefile new file mode 100644 index 0000000..e843bc1 --- /dev/null +++ b/monitor/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=args.c monitor.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/monitor/args.c b/monitor/args.c new file mode 100644 index 0000000..29669d6 --- /dev/null +++ b/monitor/args.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include + +// +// Scheduler includes +// +#include "FreeRTOS.h" +#include "args.h" + +// +// +// +static char *strtrim (char *s) +{ + char *t = s + strlen (s) - 1; + + while (t >= s && *t && isspace (*t)) + *t-- = '\0'; + + while (*s && isspace (*s)) + s++; + + return s; +} + +// +// bufferLength includes the space reserved for the \0 +// +int argsGetLine (int fd __attribute__ ((unused)), U8 *buffer, int bufferLength) +{ + U8 *p; + + p = buffer; + *p = '\0'; + + while (1) + { + U8 c; + + fflush (stdout); + + if (read (fd, &c, sizeof (c)) == sizeof (c)) + { + switch (c) + { + case '\n' : + case '\r' : + printf ("\n"); + return strlen ((char *) buffer); + + case '\b' : + if (p > buffer) + *--p = '\0'; + printf ("\b \b"); + break; + + case 0x15 : // CTRL-U + while (p != buffer) + { + printf ("\b \b"); + --p; + } + *p = '\0'; + break; + + case 0xfe : + case 0xff : + *buffer++ = c; + *buffer = '\0'; + return 1; + + default : + if (p < buffer + bufferLength - 1 && c >= ' ' && c < 0x7f) + { + *p++ = c; + *p = '\0'; + printf ("%c", c); + } + else + printf ("%c", c); + + break; + } + } + } + + return 0; +} + +// +// +// +typedef enum +{ + P_EATWHITESPACE = 0, + P_GETCHARFIRST, + P_GETCHAR, + P_QUOTEDGETCHAR +} +PSTATE; + +int argsParse (char *cmd, char **argv, int sizeofArgv, int *argc) +{ + int maxArgs = (sizeofArgv / sizeof (argv [0])) - 1; + char *s = strtrim (cmd); + PSTATE pstate = P_EATWHITESPACE; + + *argc = 0; + memset (argv, 0, sizeofArgv); + + while (*s) + { + switch (pstate) + { + case P_EATWHITESPACE : + { + if (!isspace (*s)) + pstate = P_GETCHARFIRST; + else + s++; + } + break; + + case P_GETCHARFIRST : + { + *argv++ = s; + + if (++*argc == maxArgs) + return 1; + if (*s == '"') + pstate = P_QUOTEDGETCHAR; + else + pstate = P_GETCHAR; + + s++; + } + break; + + case P_GETCHAR : + { + if (isspace (*s)) { + pstate = P_EATWHITESPACE; + *s = '\0'; + } + else if (*s == '"') + pstate = P_QUOTEDGETCHAR; + + s++; + } + break; + + case P_QUOTEDGETCHAR : + { + if (*s == '"') + pstate = P_GETCHAR; + + s++; + } + break; + } + } + + return 0; +} diff --git a/monitor/args.h b/monitor/args.h new file mode 100644 index 0000000..3767fc8 --- /dev/null +++ b/monitor/args.h @@ -0,0 +1,7 @@ +#ifndef _ARGS_H_ +#define _ARGS_H_ + +int argsGetLine (int fd, U8 *buffer, int bufferLength); +int argsParse (char *cmd, char **argv, int sizeofArgv, int *argc); + +#endif diff --git a/monitor/monitor.c b/monitor/monitor.c new file mode 100644 index 0000000..ba5292b --- /dev/null +++ b/monitor/monitor.c @@ -0,0 +1,2426 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "args.h" +#include "../fatfs/diskio.h" +#include "../fatfs/ff.h" +#include "../fiq/fiq.h" +#include "../gps/gps.h" +#include "../i2c/eeprom.h" +#include "../i2c/i2c.h" +#include "../i2c/lm75.h" +#include "../iap/iap.h" +#include "../main.h" +#include "../rtc/rtc.h" +#include "../sensors/sensors.h" +#include "../swi/swi.h" +#include "monitor.h" + +// +// +// +typedef enum +{ + CMDTYPE_CMDLIST = 0, + CMDTYPE_FUNCTION +} +cmdType_e; + +typedef struct abortDat_s +{ + unsigned int dummy; + unsigned int sigil; + unsigned int count; + unsigned int type; + unsigned int pc; + unsigned int opcode; + unsigned int cpsr; + unsigned int lr; + unsigned int sp; + unsigned int r0; + unsigned int r1; + unsigned int r2; + unsigned int r3; + unsigned int r4; + unsigned int r5; + unsigned int r6; + unsigned int r7; + unsigned int r8; + unsigned int r9; + unsigned int r10; + unsigned int r11; + unsigned int r12; + unsigned int stack [8]; +} +__attribute__ ((packed)) abortDat_t; + +typedef struct commandList_s +{ + const portCHAR *command; + portCHAR minArgs; + portCHAR maxArgs; + cmdType_e cmdType; + union + { + void *trickGCC; + int (*handler) (int argc, portCHAR **argv); + struct commandList_s *commandList; + }; + const portCHAR *description; + const portCHAR *parameters; +} +commandList_t; + +// +// Prototypes +// +static int monitorHelp (int argc, portCHAR **argv); +static int monitorDiskInit (int argc, portCHAR **argv); +static int monitorDiskMount (int argc, portCHAR **argv); +static int monitorFileMkfs (int argc, portCHAR **argv); +static int monitorFileDf (int argc, portCHAR **argv); +static int monitorFileLs (int argc, portCHAR **argv); +static int monitorFileMkdir (int argc, portCHAR **argv); +static int monitorFileRmdir (int argc, portCHAR **argv); +static int monitorFileRm (int argc, portCHAR **argv); +static int monitorFileMv (int argc, portCHAR **argv); +static int monitorFileCp (int argc, portCHAR **argv); +static int monitorFileCpCon (int argc, portCHAR **argv); +static int monitorFileChmod (int argc, portCHAR **argv); +static int monitorFileSync (int argc, portCHAR **argv); +static int monitorFileThruPut (int argc, portCHAR **argv); +static int monitorGPS (int argc, portCHAR **argv); +static int monitorSensors (int argc, portCHAR **argv); +static int monitorMd (int argc, portCHAR **argv); +static int monitorAbortRegs (int argc, portCHAR **argv); +static int monitorAbortClear (int argc, portCHAR **argv); +static int monitorAbortDirty (int argc, portCHAR **argv); +static int monitorAbortUndef (int argc, portCHAR **argv); +static int monitorAbortPabort (int argc, portCHAR **argv); +static int monitorAbortDabort (int argc, portCHAR **argv); +static int monitorEEAddr (int argc, portCHAR **argv); +static int monitorEERead (int argc, portCHAR **argv); +static int monitorEEReadAddr (int argc, portCHAR **argv); +static int monitorEEWrite (int argc, portCHAR **argv); +static int monitorEEWriteAddr (int argc, portCHAR **argv); +static int monitorEEFillAddr (int argc, portCHAR **argv); +static int monitorFIQOn (int argc, portCHAR **argv); +static int monitorFIQOff (int argc, portCHAR **argv); +static int monitorFIQCount (int argc, portCHAR **argv); +static int monitorFIQClear (int argc, portCHAR **argv); +static int monitorI2CRead (int argc, portCHAR **argv); +static int monitorI2CWrite (int argc, portCHAR **argv); +static int monitorI2CWriteRead (int argc, portCHAR **argv); +static int monitorI2CDump (int argc, portCHAR **argv); +static int monitorI2CErrno (int argc, portCHAR **argv); +static int monitorIAPFSS (int argc, portCHAR **argv); +static int monitorIAPSTOA (int argc, portCHAR **argv); +static int monitorIAPFill (int argc, portCHAR **argv); +static int monitorIAPErase (int argc, portCHAR **argv); +static int monitorIAPBlank (int argc, portCHAR **argv); +static int monitorIAPID (int argc, portCHAR **argv); +static int monitorIAPVer(int argc, portCHAR **argv); +static int monitorIAPISP (int argc, portCHAR **argv); +static int monitorLM75Init (int argc, portCHAR **argv); +static int monitorLM75Mode (int argc, portCHAR **argv); +static int monitorLM75Addr (int argc, portCHAR **argv); +static int monitorLM75ReRead (int argc, portCHAR **argv); +static int monitorLM75Temp (int argc, portCHAR **argv); +static int monitorLM75Config (int argc, portCHAR **argv); +static int monitorLM75Thyst (int argc, portCHAR **argv); +static int monitorLM75Tos (int argc, portCHAR **argv); +static int monitorMemTask (int argc, portCHAR **argv); +static int monitorMemMap (int argc, portCHAR **argv); +static int monitorMemAlloc (int argc, portCHAR **argv); +static int monitorMemRealloc (int argc, portCHAR **argv); +static int monitorMemFree (int argc, portCHAR **argv); +static int monitorMemList (int argc, portCHAR **argv); +static int monitorMiscSizeof (int argc, portCHAR **argv); +static int monitorRTCGet (int argc, portCHAR **argv); +static int monitorRTCSet (int argc, portCHAR **argv); +static int monitorRTCAlarm (int argc, portCHAR **argv); +static int monitorRTCPeriodic (int argc, portCHAR **argv); +static int monitorSWISetAsm (int argc, portCHAR **argv); +static int monitorSWIOnAsm (int argc, portCHAR **argv); +static int monitorSWIOffAsm (int argc, portCHAR **argv); +static int monitorSWIToggleAsm (int argc, portCHAR **argv); +static int monitorSWISetC (int argc, portCHAR **argv); +static int monitorSWIOnC (int argc, portCHAR **argv); +static int monitorSWIOffC (int argc, portCHAR **argv); +static int monitorSWIToggleC (int argc, portCHAR **argv); +static int monitorWDTTest (int argc, portCHAR **argv); +static int monitorWDTStatus (int argc, portCHAR **argv); +static int monitorWDTClear (int argc, portCHAR **argv); +static int monitorVersion (int argc, portCHAR **argv); + +// +// Ye olde globals +// +static commandList_t commandListAbort [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "regs", 0, 0, CMDTYPE_FUNCTION, { monitorAbortRegs }, "Print abort registers", "'regs' has no parameters" }, + { "clear", 0, 0, CMDTYPE_FUNCTION, { monitorAbortClear }, "Clear abort registers", "'clear' has no parameters" }, + { "dirty", 0, 0, CMDTYPE_FUNCTION, { monitorAbortDirty }, "Dirty sigil flag", "'dirty' has no parameters" }, + { "undef", 0, 0, CMDTYPE_FUNCTION, { monitorAbortUndef }, "Execute undefined instruction","'undef' has no parameters" }, + { "pabort", 0, 0, CMDTYPE_FUNCTION, { monitorAbortPabort }, "Cause prefetch abort", "'pabort' has no parameters" }, + { "dabort", 0, 0, CMDTYPE_FUNCTION, { monitorAbortDabort }, "Cause data abort", "'dabort' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListEE [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "a", 1, 1, CMDTYPE_FUNCTION, { monitorEEAddr }, "Set eeprom r/w address", "'ee
'" }, + { "r", 0, 1, CMDTYPE_FUNCTION, { monitorEERead }, "Read from current address", "'r <# bytes>'" }, + { "ra", 1, 2, CMDTYPE_FUNCTION, { monitorEEReadAddr }, "Read EEPROM", "'ra
<# bytes>'" }, + { "w", 1, 16, CMDTYPE_FUNCTION, { monitorEEWrite }, "Write to current address", "'w [ [...]]'" }, + { "wa", 2, 17, CMDTYPE_FUNCTION, { monitorEEWriteAddr }, "Write EEPOM", "'wa
[ [...]]'" }, + { "fa", 3, 3, CMDTYPE_FUNCTION, { monitorEEFillAddr }, "Fill EEPOM", "'fa
'" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListFIQ [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "on", 0, 0, CMDTYPE_FUNCTION, { monitorFIQOn }, "Enable FIQ interrupt", "'on' has no parameters" }, + { "off", 0, 0, CMDTYPE_FUNCTION, { monitorFIQOff }, "Disable FIQ interrupt", "'off' has no parameters" }, + { "count", 0, 0, CMDTYPE_FUNCTION, { monitorFIQCount }, "Show number of FIQ interrupts", "'show' has no parameters" }, + { "clear", 0, 0, CMDTYPE_FUNCTION, { monitorFIQClear }, "Clear FIQ interrupt counter", "'clear' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListI2C [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "r", 2, 2, CMDTYPE_FUNCTION, { monitorI2CRead }, "Read from I2C device", "'r
<# bytes>'" }, + { "w", 2, 17, CMDTYPE_FUNCTION, { monitorI2CWrite }, "Write to I2C device", "'w
[ [...]]'" }, + { "wr", 2, 18, CMDTYPE_FUNCTION, { monitorI2CWriteRead}, "Write to then read from I2C device", "'wr
[ [...]] <# bytes to read>'" }, + { "dump", 0, 0, CMDTYPE_FUNCTION, { monitorI2CDump }, "Dump I2C Debug Buffer", "'dump' has no parameters" }, + { "errno", 0, 0, CMDTYPE_FUNCTION, { monitorI2CErrno }, "Display i2cErrno value", "'errno' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListIAP [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "fss", 0, 0, CMDTYPE_FUNCTION, { monitorIAPFSS }, "Find safe sector", "'fss' has no parameters" }, + { "stoa", 1, 1, CMDTYPE_FUNCTION, { monitorIAPSTOA }, "Convert sector to address for 'md'", "'fss' has no parameters" }, + { "fill", 2, 2, CMDTYPE_FUNCTION, { monitorIAPFill }, "Fill sector with byte", "'fill '" }, + { "erase", 1, 1, CMDTYPE_FUNCTION, { monitorIAPErase }, "Erase sector", "'erase '" }, + { "blank", 1, 1, CMDTYPE_FUNCTION, { monitorIAPBlank }, "Blank check sector", "'blank '" }, + { "id", 0, 0, CMDTYPE_FUNCTION, { monitorIAPID }, "Read part ID", "'id' has no parameters" }, + { "ver", 0, 0, CMDTYPE_FUNCTION, { monitorIAPVer }, "Read boot loader version", "'ver' has no parameters" }, + { "isp", 0, 0, CMDTYPE_FUNCTION, { monitorIAPISP }, "Restart into ISP bootloader", "'isp' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListLM75 [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "init", 0, 0, CMDTYPE_FUNCTION, { monitorLM75Init }, "Initialize LM75", "'init' has no parameters" }, + { "mode", 1, 1, CMDTYPE_FUNCTION, { monitorLM75Mode }, "Set LM75 mode", "'mode '" }, + { "addr", 1, 1, CMDTYPE_FUNCTION, { monitorLM75Addr }, "Set LM75 address", "'addr '" }, + { "reread", 0, 0, CMDTYPE_FUNCTION, { monitorLM75ReRead }, "Re-read last register", "'reread' has no parameters" }, + { "temp", 0, 0, CMDTYPE_FUNCTION, { monitorLM75Temp }, "Read LM75 temperature", "'temp' has no parameters" }, + { "config", 0, 1, CMDTYPE_FUNCTION, { monitorLM75Config }, "Read part ID", "'id' has no parameters" }, + { "thyst", 0, 1, CMDTYPE_FUNCTION, { monitorLM75Thyst }, "Read or set THYST register", "'thyst [value]'" }, + { "tos", 0, 1, CMDTYPE_FUNCTION, { monitorLM75Tos }, "Read or set TOS register", "'tos [value]'" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListMem [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "task", 0, 0, CMDTYPE_FUNCTION, { monitorMemTask }, "Show FreeRTOS task memory", "'task' has no parameters" }, + { "map", 0, 0, CMDTYPE_FUNCTION, { monitorMemMap }, "Show various addresses", "'map' has no parameters" }, + { "alloc", 2, 2, CMDTYPE_FUNCTION, { monitorMemAlloc }, "Allocate memory", "'alloc '" }, + { "realloc", 2, 2, CMDTYPE_FUNCTION, { monitorMemRealloc }, "Reallocate memory", "'realloc '" }, + { "free", 1, 1, CMDTYPE_FUNCTION, { monitorMemFree }, "Free memory", "'free '" }, + { "list", 0, 0, CMDTYPE_FUNCTION, { monitorMemList }, "List memory", "'list' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListMisc [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "sizeof", 0, 0, CMDTYPE_FUNCTION, { monitorMiscSizeof }, "Sizeof() variable data types", "'sizeof' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListRTC [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "get", 0, 0, CMDTYPE_FUNCTION, { monitorRTCGet }, "Display system date/time", "'get' has no parameters" }, + { "set", 1, 2, CMDTYPE_FUNCTION, { monitorRTCSet }, "Set system date/time", "'set '" }, + { "alarm", 0, 2, CMDTYPE_FUNCTION, { monitorRTCAlarm }, "Set date/time for alarm", "'alarm '" }, + { "periodic", 0, 1, CMDTYPE_FUNCTION, { monitorRTCPeriodic }, "Alarm every minute change", "'periodic '" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListSWI [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "aset", 1, 1, CMDTYPE_FUNCTION, { monitorSWISetAsm }, "Set LED2 state (asm)", "'aset '" }, + { "aon", 0, 0, CMDTYPE_FUNCTION, { monitorSWIOnAsm }, "Turn LED2 on (asm)", "'aon' has no parameters" }, + { "aoff", 0, 0, CMDTYPE_FUNCTION, { monitorSWIOffAsm }, "Turn LED2 off (asm)", "'aoff' has no parameters" }, + { "atoggle", 0, 0, CMDTYPE_FUNCTION, { monitorSWIToggleAsm}, "Toggle LED2 state (asm)", "'atoggle' has no parameters" }, + { "cset", 1, 1, CMDTYPE_FUNCTION, { monitorSWISetC }, "Set LED2 state (C)", "'cset '" }, + { "con", 0, 0, CMDTYPE_FUNCTION, { monitorSWIOnC }, "Turn LED2 on (C)", "'con' has no parameters" }, + { "coff", 0, 0, CMDTYPE_FUNCTION, { monitorSWIOffC }, "Turn LED2 off (C)", "'coff' has no parameters" }, + { "ctoggle", 0, 0, CMDTYPE_FUNCTION, { monitorSWIToggleC }, "Toggle LED2 state (C)", "'ctoggle' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandListWDT [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "test", 0, 0, CMDTYPE_FUNCTION, { monitorWDTTest }, "Test watchdog", "'test' has no parameters" }, + { "status", 0, 0, CMDTYPE_FUNCTION, { monitorWDTStatus }, "Display RSIR register", "'status' has no parameters" }, + { "clear", 0, 0, CMDTYPE_FUNCTION, { monitorWDTClear }, "Clear RSIR status", "'rsir' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static commandList_t commandList [] = +{ + { "help", 0, 0, CMDTYPE_FUNCTION, { monitorHelp }, "This help list", "'help' has no parameters" }, + { "init", 0, 0, CMDTYPE_FUNCTION, { monitorDiskInit }, "Initialize disk subsystem", "'init' has no parameters" }, + { "mount", 0, 0, CMDTYPE_FUNCTION, { monitorDiskMount }, "Mount disk", "'mount' has no parameters" }, + { "mkfs", 0, 0, CMDTYPE_FUNCTION, { monitorFileMkfs }, "Create a FAT filesystem", "'mkfs' has no parameters" }, + { "df", 0, 0, CMDTYPE_FUNCTION, { monitorFileDf }, "File system status & info", "'fd' has no parameters" }, + { "ls", 0, 1, CMDTYPE_FUNCTION, { monitorFileLs }, "Display directory", "'ls [optional path]'" }, + { "mkdir", 1, 1, CMDTYPE_FUNCTION, { monitorFileMkdir }, "Create directory", "'mkdir <[path]directory'" }, + { "rmdir", 1, 1, CMDTYPE_FUNCTION, { monitorFileRmdir }, "Delete directory", "'rmdir <[path]directory>'" }, + { "rm", 1, 1, CMDTYPE_FUNCTION, { monitorFileRm }, "Delete file", "'rm <[path]filename>'" }, + { "mv", 2, 2, CMDTYPE_FUNCTION, { monitorFileMv }, "Rename a file or directory", "'mv <[path]old_filename> <[path]new_filename>'" }, + { "cp", 1, 2, CMDTYPE_FUNCTION, { monitorFileCp }, "Copy a file", "'mv <[path]old_filename> <[path]new_filename>'" }, + { "cpcon", 1, 1, CMDTYPE_FUNCTION, { monitorFileCpCon }, "Copy console input to file", "'cp <[path]filename>' (ctrl-d exits and saves)" }, + { "chmod", 2, 2, CMDTYPE_FUNCTION, { monitorFileChmod }, "Change file mode", "'chmod <+w|-w> <[path]filename>'" }, + { "sync", 0, 0, CMDTYPE_FUNCTION, { monitorFileSync }, "Sync filesystem", "'sync' has no parameters" }, + { "thruput", 1, 1, CMDTYPE_FUNCTION, { monitorFileThruPut }, "Measure MMC/SD throughput", "'thruput '" }, + { "gps", 0, 0, CMDTYPE_FUNCTION, { monitorGPS }, "Display GPS data", "'gps' has no parameters" }, + { "sensors", 0, 0, CMDTYPE_FUNCTION, { monitorSensors }, "Display sensor data", "'sensors' has no parameters" }, + { "md", 0, 2, CMDTYPE_FUNCTION, { monitorMd }, "Display memory", "'md [address [length]]'" }, + + { "abort", 1, 0, CMDTYPE_CMDLIST, { commandListAbort }, "Read/clear abort registers", "'abort help' for help list" }, + { "ee", 1, 0, CMDTYPE_CMDLIST, { commandListEE }, "Read/write I2C EEPROM", "'ee help' for help list" }, + { "fiq", 1, 0, CMDTYPE_CMDLIST, { commandListFIQ }, "Demonstrate FIQ functions", "'fiq help' for help list" }, + { "i2c", 1, 0, CMDTYPE_CMDLIST, { commandListI2C }, "Perform I2C commands", "'i2c help' for help list" }, + { "iap", 1, 0, CMDTYPE_CMDLIST, { commandListIAP }, "Perform IAP commands", "'iap help' for help list" }, + { "lm75", 1, 0, CMDTYPE_CMDLIST, { commandListLM75 }, "Read/set LM75 temp sensor", "'lm75 help' for help list" }, + { "mem", 1, 0, CMDTYPE_CMDLIST, { commandListMem }, "Various memory functions", "'mem help' for help list" }, + { "misc", 1, 0, CMDTYPE_CMDLIST, { commandListMisc }, "Miscellaneous stuff", "'misc help' for help list" }, + { "rtc", 1, 0, CMDTYPE_CMDLIST, { commandListRTC }, "Demonstrate RTC functions", "'rtc help' for help list" }, + { "swi", 1, 0, CMDTYPE_CMDLIST, { commandListSWI }, "Demonstrate SWI functions", "'swi help' for help list" }, + { "wdt", 1, 0, CMDTYPE_CMDLIST, { commandListWDT }, "Manipulate watchdog timer", "'wdt help' for help list" }, + { "version", 0, 0, CMDTYPE_FUNCTION, { monitorVersion }, "Display version information", "'version' has no parameters" }, + { NULL, 0, 0, CMDTYPE_FUNCTION, { NULL }, NULL, NULL }, +}; + +static FATFS fatfs; +static FILINFO fileInfo; +commandList_t *activeCommandList = NULL; + +// +// External variables +// +extern unsigned int __abort_dat; +extern unsigned long __start_of_text__; +extern unsigned long __end_of_text__; +extern unsigned long __start_of_startup__; +extern unsigned long __end_of_startup__; +extern unsigned long __start_of_prog__; +extern unsigned long __end_of_prog__; +extern unsigned long __start_of_rodata__; +extern unsigned long __end_of_rodata__; +extern unsigned long __start_of_glue7__; +extern unsigned long __end_of_glue7__; +extern unsigned long __data_beg__; +extern unsigned long __data_end__; +extern unsigned long __bss_beg__; +extern unsigned long __bss_end__; +extern unsigned long __heap_max; +extern unsigned long __heap_beg; +extern unsigned long __heap_end; +extern unsigned long __stack_end__; +extern unsigned long __stack_beg_und; +extern unsigned long __stack_end_und; +extern unsigned long __stack_beg_abt; +extern unsigned long __stack_end_abt; +extern unsigned long __stack_beg_fiq; +extern unsigned long __stack_end_fiq; +extern unsigned long __stack_beg_irq; +extern unsigned long __stack_end_irq; +extern unsigned long __stack_beg_svc; +extern unsigned long __stack_end_svc; +extern unsigned long __stack_beg_sys; +extern unsigned long __stack_end_sys; + + +// +// Recursion is our friend... fileInfo is global to avoid eating stack space +// +static FRESULT scan_files_ex (char *path, int *total_size, int *total_files, int *total_dirs) +{ + DIR dirs; + FRESULT res; + + if ((res = f_opendir (&dirs, path)) == FR_OK) + { + int i = strlen (path); + + while (((res = f_readdir (&dirs, &fileInfo)) == FR_OK) && fileInfo.fname [0]) + { + if (fileInfo.fattrib & AM_DIR) + { + *total_dirs += 1; + *(path + i) = '/'; + strcpy (path + i + 1, &fileInfo.fname [0]); + res = scan_files_ex (path, total_size, total_files, total_dirs); + *(path + i) = '\0'; + + if (res != FR_OK) + return res; + } + else + { + *total_files += 1; + *total_size += fileInfo.fsize; + } + } + } + + return res; +} + +static FRESULT scan_files (char *path, int *total_size, int *total_files, int *total_dirs) +{ + *total_size = 0; + *total_files = 0; + *total_dirs = 0; + + return scan_files_ex (path, total_size, total_files, total_dirs); +} + +// +// These two really ought to be in the FatFS code +// +U32 get_fattime () +{ + U32 tmr; + time_t now; + struct tm tm; + + now = time (NULL); + localtime_r (&now, &tm); + + tmr = 0 + | ((tm.tm_year - 80) << 25) + | ((tm.tm_mon + 1) << 21) + | (tm.tm_mday << 16) + | (tm.tm_hour << 11) + | (tm.tm_min << 5) + | (tm.tm_sec >> 1); + + return tmr; +} + +// +// Functions newlib doesn't know about (but should) +// +void _sync (void); +int _mkdir (const char *path, mode_t mode); +int _chmod (const char *path, mode_t mode); + +void sync (void); +int chmod (const char *path, mode_t mode); + +void sync (void) +{ + _sync (); +} + +int mkdir (const char *path, mode_t mode) +{ + return _mkdir (path, mode); +} + +int chmod (const char *path, mode_t mode) +{ + return _chmod (path, mode); +} + +// +// +// +static int getNumber (char *s, unsigned int *result) +{ + unsigned int value; + unsigned int mustBeHex = FALSE; + int sgn = 1; + const unsigned char hexToDec [] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15}; + + if (!s) + return 0; + + if ((strlen (s) > 2) && (!strncmp (s, "0x", 2) || !strncmp (s, "0X", 2))) + { + mustBeHex = TRUE; + s += 2; + } + + if (!mustBeHex && *s && (*s == '-')) + { + sgn = -1; + s++; + } + + for (value = 0; *s; s++) + { + if (mustBeHex && isxdigit (*s)) + value = (value << 4) | hexToDec [toupper (*s) - '0']; + else if (isdigit (*s)) + value = (value * 10) + (*s - '0'); + else + { + printf ("Malformed number. Must be decimal number, or hex value preceeded by '0x'\n"); + return 0; + } + } + + if (!mustBeHex) + value *= sgn; + + *result = value; + + return 1; +} + +static int monitorDumpMemory (unsigned int displayAddress, unsigned int address, int length) +{ + unsigned char *buffer; + int i; + + if (!length) + { + printf ("Error: monitorDumpMemory() passed 0 for length\n"); + return address; + } + + for (buffer = (unsigned char *) address, i = 0; i < length; i += 16) + { + unsigned int l; + unsigned int j; + + if (i) + printf ("\n"); + + printf ("%08x: ", displayAddress + i); + + if ((length - i) < 16) + l = length & 15; + else + l = 16; + + for (j = 0; j < 16; j++) + { + if (j < l) + printf ("%02x ", buffer [i+j]); + else + printf (" "); + } + + printf (" "); + + for (j = 0; j < l; j++) + { + unsigned char c = buffer [i+j]; + + if (c < 32 || c > 127) + c = '.'; + + printf ("%c", c); + } + } + + printf ("\n"); + + address += length; + + return address; +} + +// +// +// +static int monitorHelp (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + unsigned int i; + int t; + int longestCmd; + portCHAR spaces [32]; + + memset (spaces, ' ', sizeof (spaces)); + + for (longestCmd = 0, i = 0; activeCommandList [i].command; i++) + if ((t = strlen (activeCommandList [i].command)) > longestCmd) + longestCmd = t; + + spaces [longestCmd] = '\0'; + + for (i = 0; activeCommandList [i].command; i++) + { + const commandList_t *cl = &activeCommandList [i]; + + printf ("%s%s -- %s\n", cl->command, &spaces [strlen (cl->command)], cl->description); + } + + printf ("\nUse ' ?' for details on parameters to command\n"); + + return 0; +} + +// +// +// +static int monitorDiskInit (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("RRC=%d\n", diskInitialize (0)); + + return 0; +} + +static int monitorDiskMount (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + f_printerror (f_mount (0, &fatfs)); + + return 0; +} + +static int monitorFileMkfs (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + f_printerror (f_mkfs (0, 0, 64)); + + return 0; +} + +static int monitorFileDf (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + U32 p2; + FATFS *fs; + char buffer [100]; + FRESULT res; + int acc_size; + int acc_files; + int acc_dirs; + + if ((res = f_getfree ("", (U32 *) &p2, &fs))) + { + f_printerror (res); + return 0; + } + + printf ("FAT type = %u\nBytes/Cluster = %u\nNumber of FATs = %u\n" + "Root DIR entries = %u\nSectors/FAT = %u\nNumber of clusters = %u\n" + "FAT start (lba) = %u\nDIR start (lba,clustor) = %u\nData start (lba) = %u\n", + fs->fs_type, fs->sects_clust * 512, fs->n_fats, + fs->n_rootdir, fs->sects_fat, fs->max_clust - 2, + fs->fatbase, fs->dirbase, fs->database + ); + + acc_size = acc_files = acc_dirs = 0; + + buffer [0] = '\0'; + + if ((res = scan_files (buffer, &acc_size, &acc_files, &acc_dirs))) + { + f_printerror (res); + return 0; + } + + printf ("\n%u files, %u bytes.\n%u folders.\n" + "%u bytes total disk space.\n%u bytes available\n", + acc_files, acc_size, acc_dirs, + (fs->max_clust - 2) * fs->sects_clust * 512, p2 * fs->sects_clust * 512 + ); + + return 0; +} + +static int monitorFileLs (int argc, portCHAR **argv) +{ + DIR dir; + FRESULT res; + U32 size; + U16 files; + U16 dirs; + FATFS *fs; + char *path; + + path = argc ? argv [0] : ""; + + if ((res = f_opendir (&dir, path))) + { + f_printerror (res); + return 0; + } + + for (size = files = dirs = 0;;) + { + if (((res = f_readdir (&dir, &fileInfo)) != FR_OK) || !fileInfo.fname [0]) + break; + + if (fileInfo.fattrib & AM_DIR) + dirs++; + else + { + files++; + size += fileInfo.fsize; + } + + printf ("\n%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u %s", + (fileInfo.fattrib & AM_DIR) ? 'D' : '-', + (fileInfo.fattrib & AM_RDO) ? 'R' : '-', + (fileInfo.fattrib & AM_HID) ? 'H' : '-', + (fileInfo.fattrib & AM_SYS) ? 'S' : '-', + (fileInfo.fattrib & AM_ARC) ? 'A' : '-', + (fileInfo.fdate >> 9) + 1980, (fileInfo.fdate >> 5) & 15, fileInfo.fdate & 31, + (fileInfo.ftime >> 11), (fileInfo.ftime >> 5) & 63, + fileInfo.fsize, &(fileInfo.fname [0])); + } + + printf ("\n%4u File(s),%10u bytes\n%4u Dir(s)", files, size, dirs); + + if (f_getfree (path, (U32 *) &size, &fs) == FR_OK) + printf (", %10uK bytes free", size * fs->sects_clust / 2); + + printf ("\n"); + + return 0; +} + +static int monitorFileMkdir (int argc __attribute__ ((unused)), portCHAR **argv) +{ + f_printerror (f_mkdir (argv [0])); + + return 0; +} + +static int monitorFileRmCommon (char *path, int mode) +{ + FRESULT f; + + if ((f = f_stat (path, &fileInfo)) != FR_OK) + { + f_printerror (f); + return 0; + } + + if (mode == AM_DIR) + { + if (!(fileInfo.fattrib & AM_DIR)) + printf ("Not a directory\n"); + else + f_printerror (f_unlink (path)); + } + else + { + if (fileInfo.fattrib & AM_DIR) + printf ("Not a regular file\n"); + else + f_printerror (f_unlink (path)); + } + + return 0; +} + +static int monitorFileRmdir (int argc __attribute__ ((unused)), portCHAR **argv) +{ + return monitorFileRmCommon (argv [0], AM_DIR); +} + +static int monitorFileRm (int argc __attribute__ ((unused)), portCHAR **argv) +{ + return monitorFileRmCommon (argv [0], 0); +} + +static int monitorFileMv (int argc __attribute__ ((unused)), portCHAR **argv) +{ + if (rename (argv [0], argv [1]) == -1) + printf ("rename failed, errno=%d/%s\n", errno, strerror (errno)); + + return 0; +} + +static int monitorFileCp (int argc __attribute__ ((unused)), portCHAR **argv) +{ + int fdIn; + int fdOut; + int l; + char buffer [128]; + + if ((fdIn = open (argv [0], O_RDONLY)) == -1) + { + printf ("Cannot open input file \"%s\", errno=%d/%s\n", argv [0], errno, strerror (errno)); + return 0; + } + + if (argc == 1) + { + fdOut = fileno (stdout); + fflush (stdout); + } + else if ((fdOut = open (argv [1], O_CREAT | O_TRUNC | O_WRONLY)) == -1) + { + printf ("Cannot open output file \"%s\", errno=%d/%s\n", argv [1], errno, strerror (errno)); + close (fdIn); + return 0; + } + + while ((l = read (fdIn, buffer, sizeof (buffer)))) + { + if (write (fdOut, buffer, l) != l) + { + printf ("write() returned error %d/%s (l=%d)\n", errno, strerror (errno), l); + break; + } + } + + close (fdIn); + + if (fdOut != fileno (stdout)) + close (fdOut); + + return 0; +} + +static int monitorFileSync (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + sync (); + + return 0; +} + +int monitorTimevalSubtract (struct timeval *result, struct timeval *x, struct timeval *y); +int monitorTimevalSubtract (struct timeval *result, struct timeval *x, struct timeval *y) +{ + if (x->tv_usec < y->tv_usec) + { + int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + + if (x->tv_usec - y->tv_usec > 1000000) + { + int nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + return x->tv_sec < y->tv_sec; +} + +typedef enum +{ + MODE_NORMAL = 0, + MODE_NOINTS, + MODE_SUSPENDALL, + MODE_HIGH +} +mode_e; + +static int monitorFileThruPut (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int i; + mode_e mode; + int fileSizes [] = { 1*1024, 8*1024, 16*1024, 64*1024, 128*1024, 512*1024, 1024*1024, 2048*1024 }; + + if (!strcmp (argv [0], "normal")) + mode = MODE_NORMAL; + else if (!strcmp (argv [0], "noints")) + mode = MODE_NOINTS; + else if (!strcmp (argv [0], "suspendall")) + mode = MODE_SUSPENDALL; + else if (!strcmp (argv [0], "high")) + mode = MODE_HIGH; + else + { + printf ("arguments must be 'normal', 'noints', 'suspendall', or 'high'\n"); + return 0; + } + + printf ("%lu tests, with ", arrsizeof (fileSizes)); + + switch (mode) + { + case MODE_NORMAL : printf ("interrupts enabled, no tasks suspended, default priority\n\n"); break; + case MODE_NOINTS : printf ("interrupts disabled (no tasking)\n\n"); break; + case MODE_SUSPENDALL : printf ("interrupts enabled, all tasks suspended\n\n"); break; + case MODE_HIGH : printf ("interrupts enabled, this task promoted to highest priority\n\n"); + } + + for (i = 0; i < arrsizeof (fileSizes); i++) + { + int j; + + for (j = 0; j < 2; j++) + { + int fd; + int k; + char buffer [512]; + struct timeval tv_start; + struct timeval tv_end; + struct timeval tv_diff; + unsigned long ticks10ms; + unsigned portBASE_TYPE taskPriority = 0; + + if ((fd = open ("testfile.bin", !j ? (O_TRUNC | O_CREAT | O_WRONLY) : (O_RDONLY))) == -1) + { + printf ("Cannot open \"testfile.bin\", errno=%d/%s\n", errno, strerror (errno)); + return 0; + } + + if (!j) + memset (buffer, 0xe5, sizeof (buffer)); + + + switch (mode) + { + case MODE_NORMAL : break; + case MODE_NOINTS : portENTER_CRITICAL (); break; + case MODE_SUSPENDALL : vTaskSuspendAll (); break; + case MODE_HIGH : taskPriority = uxTaskPriorityGet (NULL); vTaskPrioritySet (NULL, (configMAX_PRIORITIES - 1)); break; + } + + gettimeofday (&tv_start, NULL); + + if (!j) + { + for (k = fileSizes [i]; k > 0; k -= sizeof (buffer)) + { + if (write (fd, buffer, sizeof (buffer)) != sizeof (buffer)) + { + printf ("write() failed, errno=%d/%s\n", errno, strerror (errno)); + close (fd); + return 0; + } + } + } + else + { + for (k = fileSizes [i]; k > 0; k -= sizeof (buffer)) + { + if (read (fd, buffer, sizeof (buffer)) != sizeof (buffer)) + { + printf ("read() failed, errno=%d/%s\n", errno, strerror (errno)); + close (fd); + return 0; + } + } + } + + gettimeofday (&tv_end, NULL); + + switch (mode) + { + case MODE_NORMAL : break; + case MODE_NOINTS : portEXIT_CRITICAL (); break; + case MODE_SUSPENDALL : xTaskResumeAll (); break; + case MODE_HIGH : vTaskPrioritySet (NULL, taskPriority); break; + } + + if (close (fd) == -1) + { + printf ("close() failed, errno=%d/%s\n", errno, strerror (errno)); + return 0; + } + + { + struct timeval tempStart = tv_start; + struct timeval tempEnd = tv_end; + + monitorTimevalSubtract (&tv_diff, &tv_end, &tv_start); + ticks10ms = (tv_diff.tv_sec * 100) + (tv_diff.tv_usec / 10000); + + printf ("%5s: %9u bytes, %7lu milliseconds (%4ld seconds), %7lu bytes/sec\n", + !j ? "Write" : "Read", + fileSizes [i], + ticks10ms * 10, + ticks10ms / 100, + (fileSizes [i] * 100) / ticks10ms); + + if (ticks10ms > 10000) + { + printf ("tv_start = %ld, %ld\n", tempStart.tv_sec, tempStart.tv_usec); + printf ("tv_end = %ld, %ld\n", tempEnd.tv_sec, tempEnd.tv_usec); + printf ("tv_diff = %ld, %ld\n", tv_diff.tv_sec, tv_diff.tv_usec); + } + } + + if (j && unlink ("testfile.bin") == -1) + { + printf ("unlink() failed, errno=%d/%s\n", errno, strerror (errno)); + return 0; + } + } + } + + printf ("\nDone\n"); + + return 0; +} + +static int monitorFileCpCon (int argc __attribute__ ((unused)), portCHAR **argv) +{ + int fdOut; + char c; + + if ((fdOut = open (argv [0], O_CREAT | O_TRUNC | O_WRONLY)) == -1) + { + printf ("Cannot open output file \"%s\", errno=%d/%s\n", argv [0], errno, strerror (errno)); + return 0; + } + + while (read (fileno (stdin), &c, sizeof (c))) + { + if (c == 0x04) + break; + + if (c == '\r') + c = '\n'; + + if (write (fdOut, &c, sizeof (c)) != sizeof (c)) + { + printf ("write() returned error %d/%s\n", errno, strerror (errno)); + break; + } + + printf ("%c", c); + fflush (stdout); + } + + if (close (fdOut) == -1) + printf ("close() returned error %d/%s\n", errno, strerror (errno)); + + return 0; +} + +static int monitorFileChmod (int argc __attribute__ ((unused)), portCHAR **argv) +{ + mode_t mode; + + if (!strcmp (argv [0], "-w")) + mode = 0; + else if (!strcmp (argv [0], "+w")) + mode = S_IWUSR; + else + { + printf ("mode must be +w (writable) or -w (not writable)\n"); + return 0; + } + + if (chmod (argv [1], mode) == -1) + printf ("chmod() failed, errno=%d/%s\n", errno, strerror (errno)); + + return 0; +} + +// +// +// +static int monitorGPS (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + gpsData_t gpsData; + + if (gpsCopyData (&gpsData)) + { + printf ("Valid ...... : %d\n", gpsData.valid); + printf ("Date ....... : %04d/%02d/%02d\n", gpsData.utcYear, gpsData.utcMonth, gpsData.utcDay); + printf ("Time ....... : %02d:%02d:%02d\n", gpsData.utcHours, gpsData.utcMinutes, gpsData.utcSeconds); + printf ("Speed ...... : %f\n", gpsData.groundSpeed); + printf ("Heading .... : %f\n", gpsData.trueCourse); + printf ("Altitude ... : %f\n", gpsData.height); + printf ("Latitude ... : %f\n", gpsData.latitude); + printf ("Longitude .. : %f\n", gpsData.longitude); + printf ("Restarts ... : %d\n", gpsData.restarts); + printf ("Sizeof () .. : %ld\n", sizeof (gpsData)); + } + else + printf ("Unable to get GPS data\n"); + + return 0; +} + +// +// +// +static int monitorSensors (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + sensorData_t sensorData; + + if (sensorsCopyData (&sensorData)) + { + printf ("Sensor count .. : %d\n", sensorData.sensorCount); + printf ("ADC changes ... : %d\n", sensorData.adcChanges); + } + else + printf ("Unable to get sensor data\n"); + + return 0; +} + +static int monitorMd (int argc, portCHAR **argv) +{ + static unsigned int address = 0x00000000; + unsigned int length = 256; + + if ((argc >= 1)) + { + if (!getNumber (argv [0], &address)) + return 0; + + if (argc == 2) + if (!getNumber (argv [1], &length)) + return 0; + } + + address = monitorDumpMemory (address, address, length); + + return 0; +} + +// +// +// +static int monitorAbortRegs (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + abortDat_t *ad = (abortDat_t *) &__abort_dat; + + printf ("contents=%s, sigil=0x%08x, count=%d\n", (ad->sigil == 0xdeadc0de) ? "probable" : "invalid", ad->sigil, ad->count); + printf ("abort type=%s\n", (ad->type == 0) ? "undefined instruction" : (ad->type == 1) ? "prefetch abort" : (ad->type == 2) ? "data abort" : "unknown"); + printf ("pc=0x%08x, opcode=0x%08x\n", ad->pc, ad->opcode); + printf ("cpsr=0x%08x, sp=0x%08x, lr=0x%08x\n", ad->cpsr, ad->sp, ad->lr); + printf ("r0=0x%08x, r1=0x%08x, r2=0x%08x, r3=0x%08x\n", ad->r0, ad->r1, ad->r2, ad->r3); + printf ("r4=0x%08x, r5=0x%08x, r6=0x%08x, r7=0x%08x\n", ad->r4, ad->r5, ad->r6, ad->r7); + printf ("r8=0x%08x, r9=0x%08x, r10=0x%08x, r11=0x%08x\n", ad->r8, ad->r9, ad->r10, ad->r11); + printf ("r12=0x%08x\n", ad->r12); + printf ("\n"); + + printf ("sp[0]=0x%08x, sp[1]=0x%08x, sp[2]=0x%08x, sp[3]=0x%08x\n", ad->stack [0], ad->stack [1], ad->stack [2], ad->stack [3]); + printf ("sp[4]=0x%08x, sp[5]=0x%08x, sp[6]=0x%08x, sp[7]=0x%08x\n", ad->stack [4], ad->stack [5], ad->stack [6], ad->stack [7]); + + return 0; +} + +static int monitorAbortClear (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + abortDat_t *ad = (abortDat_t *) &__abort_dat; + + memset (ad, 0, sizeof (* ad)); + + return 0; +} + +static int monitorAbortDirty (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + abortDat_t *ad = (abortDat_t *) &__abort_dat; + + ad->sigil = 0; + + return 0; +} + +static int monitorAbortUndef (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + asm volatile (" .word 0x06000010" : /* no output */ : /* no inputs */ ); + + return 0; +} + +static int monitorAbortPabort (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + asm volatile (" ldr r0, =0x00080000" : /* no output */ : /* no inputs */ ); + asm volatile (" mov pc, r0" : /* no output */ : /* no inputs */ ); + + return 0; +} + +static int monitorAbortDabort (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + unsigned char c; + volatile unsigned char *ptr = (unsigned char *) 0x40008000; + + c = *ptr; + + return 0; +} + +// +// +// +static int monitorEEAddr (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int address; + + if (!getNumber (argv [0], &address)) + return 0; + + if (eepromSetAddress (address)) + { + printf ("Error: address out of range\n"); + eepromSetAddress (0); + } + + return 0; +} + +static int monitorEERead (int argc, portCHAR **argv) +{ + unsigned int address; + unsigned int length = 256; + unsigned char buffer [64]; + unsigned int i; + + if (argc && !getNumber (argv [0], &length)) + return 0; + + for (address = eepromGetAddress(), i = 0; i < length; i += sizeof (buffer), address = (address + sizeof (buffer)) % EEPROM_SIZE) + { + unsigned int l; + + if (!(l = i % sizeof (buffer))) + l = MIN (length, sizeof (buffer)); + + if (eepromRead (buffer, l)) + { + printf ("eepromRead() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + return 0; + } + + monitorDumpMemory (address, (unsigned int) buffer, l); + } + + return 0; +} + +static int monitorEEReadAddr (int argc, portCHAR **argv) +{ + unsigned int address; + + if (!getNumber (argv [0], &address)) + return 0; + + if (eepromSetAddress (address)) + { + printf ("Error: address out of range\n"); + return 0; + } + + return monitorEERead (--argc, ++argv); +} + +static int monitorEEWriteCommon (int argc, portCHAR **argv, unsigned char *buffer, int bufferLength) +{ + int i; + + for (i = 0; i < argc; i++) + { + unsigned int n; + + if (i >= bufferLength) + { + printf ("Error: buffer too small for number arguments\n"); + return -1; + } + + if (!getNumber (argv [i], &n)) + return 0; + + if (n > 255) + { + printf ("Error: data must be 0x00..0xff (0..255)\n"); + return -1; + } + + buffer [i] = n; + } + + return 0; +} + +// +// Note the two reserved bytes at the beginning of the buffer. These are +// reserved for the address we're writing to in the EEPROM. They're populated +// by the eepromWrite() routine. This feel hackish, but unlike the read +// routines, we can't send the address, then a repeated start bit to switch to +// write. +// +static int monitorEEWrite (int argc, portCHAR **argv) +{ + unsigned char buffer [18]; + + if (monitorEEWriteCommon (argc, argv, &buffer [2], sizeof (buffer) - 2)) + return 0; + + if (eepromWrite (buffer, argc)) + printf ("eepromWrite() returned %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + + return 0; +} + +static int monitorEEWriteAddr (int argc, portCHAR **argv) +{ + unsigned int address; + + if (!getNumber (argv [0], &address)) + return 0; + + if (eepromSetAddress (address)) + { + printf ("Error: address out of range\n"); + return 0; + } + + return monitorEEWrite (--argc, ++argv); +} + +static int monitorEEFillAddr (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int address; + unsigned int length; + unsigned int fillChar; + + if (!getNumber (argv [0], &address) || !getNumber (argv [1], &length) || !getNumber (argv [2], &fillChar)) + return 0; + + if (fillChar > 255) + printf ("Error: fill value must be 0x00..0xff (0..255)\n"); + else if (eepromFillAddress (address, length, fillChar)) + printf ("eepromFillAddress() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + + return 0; +} + +// +// +// +static int monitorFIQOn (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("FIQ interrupts enabled, previous state was %s\n", fiqEnable () ? "enabled" : "disabled"); + + return 0; +} + +static int monitorFIQOff (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("FIQ interrupts disabled, previous state was %s\n", fiqDisable () ? "enabled" : "disabled"); + + return 0; +} + +static int monitorFIQCount (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("FIQ counter = %u\n", fiqGetCount ()); + + return 0; +} + +static int monitorFIQClear (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + fiqClearCount (); + + return 0; +} + +// +// +// +int monitorI2CRead (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int address; + unsigned int numBytes; + unsigned char buffer [16]; + int r; + + if (!getNumber (argv [0], &address)) + return 0; + if (!getNumber (argv [1], &numBytes)) + return 0; + + if (address > 255) + { + printf ("Error: address must be 0x00..0xff (0..255)\n"); + return 0; + } + + if ((numBytes < 1) || (numBytes > sizeof (buffer))) + { + printf ("Error: number of bytes must be 1..%ld\n", sizeof (buffer)); + return 0; + } + + r = i2cReadBuffer (address, buffer, numBytes); + + printf ("i2cReadBuffer() returned %d/%s (%s)\n\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ()), r ? "error" : "no error"); + + monitorDumpMemory (0, (unsigned int) buffer, (int) sizeof (buffer)); + + return 0; +} + +int monitorI2CWrite (int argc, portCHAR **argv) +{ + unsigned int address; + unsigned char buffer [16]; + int i; + + if (!getNumber (argv [0], &address)) + return 0; + + if (address > 255) + { + printf ("Error: address must be 0x00..0xff (0..255)\n"); + return 0; + } + + for (i = 0; i < argc - 1; i++) + { + unsigned int n; + + if (!getNumber (argv [i + 1], &n)) + return 0; + + if (n > 255) + { + printf ("Error: data must be 0x00..0xff (0..255)\n"); + return 0; + } + + buffer [i] = n; + } + + i = i2cWriteBuffer (address, buffer, argc - 1); + + printf ("i2cWriteBuffer() returned %d/%s (%s)\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ()), i ? "error" : "no error"); + + return 0; +} + +int monitorI2CWriteRead (int argc, portCHAR **argv) +{ + unsigned int address; + unsigned int bytesToWrite; + unsigned int bytesToRead; + unsigned char buffer [16]; + unsigned int i; + + if (!getNumber (argv [0], &address)) + return 0; + + if (address > 255) + { + printf ("Error: address must be 0x00..0xff (0..255)\n"); + return 0; + } + + for (bytesToWrite = argc - 2, i = 0; i < bytesToWrite; i++) + { + unsigned int n; + + if (!getNumber (argv [i + 1], &n)) + return 0; + + if (n > 255) + { + printf ("Error: data must be 0x00..0xff (0..255)\n"); + return 0; + } + + buffer [i] = n; + } + + if (!getNumber (argv [argc - 1], &bytesToRead)) + return 0; + + if ((bytesToRead < 1) || (bytesToRead > sizeof (buffer))) + { + printf ("Error: number of bytes must be 1..%ld\n", sizeof (buffer)); + return 0; + } + + i2cWriteReadBuffer (address, buffer, bytesToWrite, bytesToRead); + + printf ("i2cWriteReadBuffer() returned %d/%s\n\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + + monitorDumpMemory (0, (unsigned int) buffer, (int) sizeof (buffer)); + + return 0; +} + +int monitorI2CDump (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + i2cDump (); + + return 0; +} + +int monitorI2CErrno (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("i2cErrno=%d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + + return 0; +} + +// +// Note that although the iapXXX calls can deal with multiple sectors, we only ever +// deal with one at a time. This simplifies the user interface. +// +static int monitorIAPFSS (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int ss; + + if ((ss = iapFindSafeSector ()) == -1) + printf ("Can't find a safe sector! All of flash in use?\n"); + else + printf ("Sector number %d is safe for IAP operations (won't overwrite any code)\n", ss); + + return 0; +} + +static int monitorIAPSTOA (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int sector; + unsigned long address; + int sectorSize; + + if (!getNumber (argv [0], §or)) + return 0; + + if (iapSectorToAddress (sector, &address, §orSize) == -1) + printf ("Sector number out of range\n"); + else + printf ("Sector %d occupies address 0x%08lx for 0x%04x bytes\n", sector, address, sectorSize); + + return 0; +} + +static int monitorIAPFill (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int sector; + unsigned int byte; + + if (!getNumber (argv [0], §or)) + return 0; + if (!getNumber (argv [1], &byte)) + return 0; + + if (!iapIsSafeSector ((int) sector)) + { + printf ("Sector number out of range or not in safe region\n"); + return 0; + } + + if (byte > 255) + { + printf ("Fill character must be 0x00..0xff (0..255)\n"); + return 0; + } + + if (iapFillSectors (sector, sector, byte) == -1) + printf ("iapFillSectors returned error %d/%s\n", iapGetErrno (), iapStrerror (iapGetErrno ())); + else + printf ("Sector %d filled with 0x%02x\n", sector, byte); + + return 0; +} + +static int monitorIAPErase (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int sector; + + if (!getNumber (argv [0], §or)) + return 0; + + if (!iapIsSafeSector ((int) sector)) + { + printf ("Sector number out of range or not in safe region\n"); + return 0; + } + + if (iapEraseSectors (sector, sector) == -1) + printf ("iapEraseSectors returned error %d/%s\n", iapGetErrno (), iapStrerror (iapGetErrno ())); + else + printf ("Sector %d erased\n", sector); + + return 0; +} + +static int monitorIAPBlank (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int sector; + + if (!getNumber (argv [0], §or)) + return 0; + + if (!iapIsValidSector ((int) sector)) + { + printf ("Sector number out of range\n"); + return 0; + } + + switch (iapBlankCheckSectors (sector, sector)) + { + case 0 : + printf ("Sector %d is blank\n", sector); + break; + + case 1 : + printf ("Sector %d is not blank\n", sector); + break; + + default : + printf ("iapBlankCheckSector returned error %d/%s\n", iapGetErrno (), iapStrerror (iapGetErrno ())); + break; + } + + return 0; +} + +static int monitorIAPID (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("IAP ID=0x%08x\n", iapReadPartID ()); + + return 0; +} + +static int monitorIAPVer (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("IAP ID=0x%08x\n", iapReadBootCodeVersion ()); + + return 0; +} + +static int monitorIAPISP (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + iapISP (); + + return 0; +} + +// +// +// +int monitorLM75Init (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + if (lm75Init ()) + printf ("lm75Init() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + + return 0; +} + +int monitorLM75Mode (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int mode; + + if (!getNumber (argv [0], &mode)) + return 0; + + if (mode > 1) + printf ("Error: mode must be 0 or 1\n"); + else + { + lm75SetMode (mode); + + if (!mode) + printf ("lm75 using I2C repeated start for write then read\n"); + else + printf ("lm75 using I2C stop then start for read\n"); + } + + return 0; +} + +int monitorLM75Addr (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int address; + + if (!getNumber (argv [0], &address)) + return 0; + + if (address > 0xff) + printf ("Error: address must be 0x00..0xff (0..255)\n"); + else + lm75SetAddress (address); + + return 0; +} + +int monitorLM75ReRead (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int value; + + if (lm75ReRead (&value)) + printf ("lm75Read() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + else + printf ("value=0x%x\n", value); + + return 0; +} + +int monitorLM75Temp (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int temp; + + if (lm75TemperatureRead (&temp)) + printf ("lm75TemperatureRead() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + else + printf ("temp=%d.%dC\n", temp >> 1, (temp & 0x01) ? 5 : 0); + + return 0; +} + +int monitorLM75Config (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int config; + + if (lm75ConfigRead (&config)) + printf ("lm75ConfigRead() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + else + printf ("config=0x%02x\n", config); + + return 0; +} + +int monitorLM75Thyst (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int thyst; + + if (!argc) + { + if (lm75THYSTRead (&thyst)) + printf ("lm75THYSTRead() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + else + printf ("THYST=%d.%dC\n", thyst / 2, (thyst & 0x01) ? 5 : 0); + } + else + { + if (!getNumber (argv [0], (unsigned int *) &thyst)) + return 0; + + if ((thyst < -55) || (thyst > 125)) + printf ("Error: THYST range is -55C to +125C\n"); + else if (lm75THYSTWrite (thyst << 1)) + printf ("lm75THYSTWrite() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + } + return 0; +} + +int monitorLM75Tos (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int tos; + + if (!argc) + { + if (lm75TOSTRead (&tos)) + printf ("lm75TOSRead() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + else + printf ("TOS=%d.%dC\n", tos / 2, (tos & 0x01) ? 5 : 0); + } + else + { + if (!getNumber (argv [0], (unsigned int *) &tos)) + return 0; + + if ((tos < -55) || (tos > 125)) + printf ("Error: TOS range is -55C to +125C\n"); + else if (lm75TOSWrite (tos << 1)) + printf ("lm75TOSWrite() returned error %d/%s\n", i2cGetErrno (), i2cStrerror (i2cGetErrno ())); + } + + return 0; +} + +// +// 6 tasks, each task needs about 42 bytes to display +// +#define monitorTask_BUFFERSIZE (6 * 42) + +static int monitorMemTask (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ +#if configUSE_TRACE_FACILITY == 1 + signed portCHAR buffer [monitorTask_BUFFERSIZE]; + int bytesFree; + int bytesUsed; + int blocksFree; + + vTaskList (buffer); + vPortUsedMem (&bytesFree, &bytesUsed, &blocksFree); + printf ("%s\nHeap size=%ld, used=%d, free=%d (%d blocks)\n", buffer, configTOTAL_HEAP_SIZE, bytesUsed, bytesFree, blocksFree); +#else + printf ("Not implemented (requires configUSE_TRACE_FACILITY in FreeRTOSConfig.h)\n"); +#endif + + return 0; +} + +typedef struct memMap_s +{ + char *desc; + int m; + unsigned int start; + unsigned int end; +} +memMap_t; + +typedef union +{ + void *v; + unsigned int i; +} +sbrkConv_t; + +static inline unsigned __get_cpsr (void) +{ + unsigned long retval; + + asm volatile (" mrs %0, cpsr" : "=r" (retval) : /* no inputs */ ); + + return retval; +} + +static int monitorMemMap (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + unsigned int i; + sbrkConv_t sbrkConv; + static memMap_t memMap [] = + { + { ".startup .....", 0, (unsigned int) &__start_of_startup__, (unsigned int) &__end_of_startup__ }, + { ".text ........", 0, (unsigned int) &__start_of_text__, (unsigned int) &__end_of_text__ }, + { ".code ........", 0, (unsigned int) &__start_of_prog__, (unsigned int) &__end_of_prog__ }, + { ".rodata ......", 0, (unsigned int) &__start_of_rodata__, (unsigned int) &__end_of_rodata__ }, + { ".data ........", 0, (unsigned int) &__data_beg__, (unsigned int) &__data_end__ }, + { ".bss .........", 0, (unsigned int) &__bss_beg__, (unsigned int) &__bss_end__ }, + { "heap .........", 1, (unsigned int) &__heap_beg, (unsigned int) &__heap_end }, + { "heap range ...", 1, (unsigned int) &__heap_beg, (unsigned int) &__heap_max }, +// { "SYS stack ....", 1, (unsigned int) &__stack_beg_sys, (unsigned int) &__stack_end_sys }, // Not relevant to FreeRTOS + { "SVC stack ....", 1, (unsigned int) &__stack_beg_svc, (unsigned int) &__stack_end_svc }, + { "IRQ stack ....", 1, (unsigned int) &__stack_beg_irq, (unsigned int) &__stack_end_irq }, + { "FIQ stack ....", 1, (unsigned int) &__stack_beg_fiq, (unsigned int) &__stack_end_fiq }, + { "abort stack ..", 1, (unsigned int) &__stack_beg_abt, (unsigned int) &__stack_end_abt }, + { "undef stack ..", 1, (unsigned int) &__stack_beg_und, (unsigned int) &__stack_end_und }, + }; + + sbrkConv.v = sbrk (0); + __heap_end = sbrkConv.i; + __stack_end_sys = sbrkConv.i; + + printf ("Section Start End Length\n"); + printf ("-------------------------------------------\n"); + + for (i = 0; i < arrsizeof (memMap); i++) + { + if (!memMap [i].m) + printf ("%s 0x%08x 0x%08x 0x%x\n", memMap [i].desc, memMap [i].start, memMap [i].end, abs (memMap [i].end - memMap [i].start)); + else + printf ("%s 0x%08x 0x%08x 0x%x\n", memMap [i].desc, *(unsigned int *) memMap [i].start, *(unsigned int *) memMap [i].end, abs (*(unsigned int *) memMap [i].end - *(unsigned int *) memMap [i].start)); + } + +#if 0 + printf ("\nProcessor mode "); + + switch ((i = __get_cpsr ()) & 0x1f) + { + case 0x10 : printf ("User"); break; + case 0x11 : printf ("FIQ"); break; + case 0x12 : printf ("IRQ"); break; + case 0x13 : printf ("Supervisor"); break; + case 0x17 : printf ("Abort"); break; + case 0x1b : printf ("Undefined"); break; + case 0x1f : printf ("System"); break; + default : printf ("Unknown"); + } + + printf (", IRQ %s", (i & 0x80) ? "disabled" : "enabled"); + printf (", FIQ %s", (i & 0x40) ? "disabled" : "enabled"); + printf (", mode %s\n", (i & 0x20) ? "THUMB" : "ARM"); +#endif + + printf ("\n"); + malloc_stats (); + + return 0; +} + +typedef struct memorySlots_s +{ + unsigned char *p; + unsigned int size; +} +memorySlots_t; + +memorySlots_t memorySlots [8]; + +static int monitorMemAlloc (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int size; + unsigned int slot; + unsigned char *p; + + if (!getNumber (argv [0], &slot) || !getNumber (argv [0], &size)) + return 0; + + if (slot >= arrsizeof (memorySlots)) + { + printf ("slot must be 0..%lu\n", arrsizeof (memorySlots) - 1); + return 0; + } + + if (memorySlots [slot].p) + printf ("Slot %d in use, free it first, or use realloc\n", slot); + else if (!(p = malloc (size))) + printf ("malloc() failed, error %d/%s\n", errno, strerror (errno)); + else + { + memorySlots [slot].p = p; + memorySlots [slot].size = size; + } + + return 0; +} + +static int monitorMemRealloc (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int size; + unsigned int slot; + unsigned char *p; + + if (!getNumber (argv [0], &slot) || !getNumber (argv [0], &size)) + return 0; + + if (slot >= arrsizeof (memorySlots)) + { + printf ("slot must be 0..%lu\n", arrsizeof (memorySlots) - 1); + return 0; + } + + if (!(p = realloc (memorySlots [slot].p, size))) + printf ("realloc() failed, error %d/%s\n", errno, strerror (errno)); + else + { + memorySlots [slot].p = p; + memorySlots [slot].size = size; + } + + return 0; +} + +static int monitorMemFree (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int slot; + + if (!getNumber (argv [0], &slot)) + return 0; + + if (slot >= arrsizeof (memorySlots)) + { + printf ("slot must be 0..%lu\n", arrsizeof (memorySlots) - 1); + return 0; + } + + if (!memorySlots [slot].p) + printf ("Can't free it, slot %d not in use\n", slot); + else + { + free (memorySlots [slot].p); + memorySlots [slot].p = NULL; + memorySlots [slot].size = 0; + } + + return 0; +} + +static int monitorMemList (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + unsigned int i; + + printf ("Slot Address Size\n"); + printf ("----------------------\n"); + + for (i = 0; i < arrsizeof (memorySlots); i++) + printf ("%4d 0x%08x %d\n", i, (unsigned int) memorySlots [i].p, memorySlots [i].size); + + return 0; +} + +// +// +// +static int monitorMiscSizeof (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("sizeof (char) = %lu\n", sizeof (char)); + printf ("sizeof (short) = %lu\n", sizeof (short)); + printf ("sizeof (int) = %lu\n", sizeof (int)); + printf ("sizeof (long) = %lu\n", sizeof (long)); + printf ("sizeof (long long) = %lu\n", sizeof (long long)); + printf ("sizeof (float) = %lu\n", sizeof (float)); + printf ("sizeof (double) = %lu\n", sizeof (double)); + printf ("sizeof (void *) = %lu\n", sizeof (void *)); + + return 0; +} + +// +// +// +static int monitorRTCGet (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + time_t now; + char buffer [32]; + + now = time (NULL); + ctime_r (&now, buffer); + printf ("%s", buffer); + + return 0; +} + +// +// This works because atoi() stops on a non-digit. We already know the +// string values are digits, because of isGoodString(). +// +static int tmSetElement (int *element, int minValue, int maxValue, int adjustValue, char *s) +{ + int v; + + if (((v = atoi (s)) < minValue) || (v > maxValue)) + { + printf ("Illegal value encountered in parameter\n"); + return FALSE; + } + + *element = (v + adjustValue); + + return TRUE; +} + +static int isGoodString (char *s, char *fmt) +{ + unsigned int i; + unsigned int l; + + if ((l = strlen (s)) != strlen (fmt)) + return FALSE; + + for (i = 0; i < l; i++, s++, fmt++) + { + if (*fmt == 'N') + { + if (!isdigit (*s)) + return FALSE; + } + else if (*fmt != *s) + return FALSE; + } + + return TRUE; +} + +static int monitorRTCSet (int argc, portCHAR **argv) +{ + if ((argc == 1) && !strcmp (argv [0], "gps")) + { + gpsData_t gpsData; + + if (gpsCopyData (&gpsData)) + { + if (gpsData.valid) + { + struct tm tm; + + tm.tm_sec = gpsData.utcSeconds; + tm.tm_min = gpsData.utcMinutes; + tm.tm_hour = gpsData.utcHours; + tm.tm_mday = gpsData.utcDay; + tm.tm_mon = gpsData.utcMonth - 1; + tm.tm_year = gpsData.utcYear - 1900; + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; + + rtcSetEpochSeconds (mktime (&tm)); + + printf ("Date/time set from GPS\n"); + } + else + printf ("GPS data not valid\n"); + } + else + printf ("Unable to get GPS data\n"); + } + else if (isGoodString (argv [0], "NNNN/NN/NN") && isGoodString (argv [1], "NN:NN:NN")) + { + struct tm tm; + + if (!tmSetElement (&tm.tm_year, 1900, 2038, -1900, &argv [0][0])) + return 0; + if (!tmSetElement (&tm.tm_mon, 1, 12, -1, &argv [0][5])) + return 0; + if (!tmSetElement (&tm.tm_mday, 1, 31, 0, &argv [0][8])) + return 0; + if (!tmSetElement (&tm.tm_hour, 0, 23, 0, &argv [1][0])) + return 0; + if (!tmSetElement (&tm.tm_min, 0, 59, 0, &argv [1][3])) + return 0; + if (!tmSetElement (&tm.tm_sec, 0, 59, 0, &argv [1][6])) + return 0; + + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; + + rtcSetEpochSeconds (mktime (&tm)); + } + else + printf ("Parameter(s) either not 'gps' or not 'YYYYY/MM/DD HH:MM:SS'\n"); + + return 0; +} + +static int monitorRTCAlarm (int argc, portCHAR **argv) +{ + if (argc == 0) + { + time_t a; + + if ((a = rtcGetAlarmEpochSeconds ())) + { + char buffer [32]; + ctime_r (&a, buffer); + printf ("%s", buffer); + } + else + printf ("alarm not enabled\n"); + } + else if (argc == 1) + { + if (!strcmp (argv [0], "off")) + rtcSetAlarm (NULL); + else + printf ("'rtc alarm' requires either 'off' or 'YYYY/MM/DD HH:MM:SS' as arguments\n"); + } + else if (isGoodString (argv [0], "NNNN/NN/NN") && isGoodString (argv [1], "NN:NN:NN")) + { + struct tm tm; + + if (!tmSetElement (&tm.tm_year, 1900, 2038, -1900, &argv [0][0])) + return 0; + if (!tmSetElement (&tm.tm_mon, 1, 12, -1, &argv [0][5])) + return 0; + if (!tmSetElement (&tm.tm_mday, 1, 31, 0, &argv [0][8])) + return 0; + if (!tmSetElement (&tm.tm_hour, 0, 23, 0, &argv [1][0])) + return 0; + if (!tmSetElement (&tm.tm_min, 0, 59, 0, &argv [1][3])) + return 0; + if (!tmSetElement (&tm.tm_sec, 0, 59, 0, &argv [1][6])) + return 0; + + if (rtcSetAlarm (&tm)) + printf ("Cannot set the alarm for the past\n"); + } + else + printf ("Parameter(s) either not 'off' or not 'YYYYY/MM/DD HH:MM:SS'\n"); + + return 0; +} + +static int monitorRTCPeriodic (int argc, portCHAR **argv) +{ + if (!argc) + printf ("Current periodic alarm state is %s\n", rtcPeriodicAlarm (-1) ? "on" : "off"); + else if (!strcmp (argv [0], "off")) + rtcPeriodicAlarm (0); + else if (!strcmp (argv [0], "on")) + rtcPeriodicAlarm (1); + else + printf ("Error: if argument present, must be 'off' or 'on'\n"); + + return 0; +} + +// +// +// +static inline unsigned long monitorSWICommon (unsigned long swi); +static inline unsigned long monitorSWICommon (unsigned long swi) +{ + unsigned long result; + + SWICALL (swi, result); + + return result; +} + +static inline unsigned long monitorSWICommon2 (unsigned long swi, unsigned long state); +static inline unsigned long monitorSWICommon2 (unsigned long swi, unsigned long state) +{ + unsigned long result; + + SWICALL1 (swi, state, result); + + return result; +} + +int monitorSWISetAsm (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int state; + + if (!strcmp (argv [0], "on")) + state = 0; + else if (!strcmp (argv [0], "off")) + state = 1; + else + { + printf ("State must be 'on' or 'off'\n"); + return 0; + } + + printf ("Setting LED2 %s via C, previous state was %s\n", state ? "off" : "on", monitorSWICommon2 (SWICALL_A_LED2SET, state) ? "off" : "on"); + + return 0; +} + +int monitorSWIOnAsm (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("Setting LED2 on via assembly, previous state was %s\n", monitorSWICommon (SWICALL_A_LED2ON) ? "off" : "on"); + + return 0; +} + +int monitorSWIOffAsm (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("Setting LED2 off via assembly, previous state was %s\n", monitorSWICommon (SWICALL_A_LED2OFF) ? "off" : "on"); + + return 0; +} + +int monitorSWIToggleAsm (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("Toggling LED2 via assembly, previous state was %s\n", monitorSWICommon (SWICALL_A_LED2TOGGLE) ? "off" : "on"); + + return 0; +} + +int monitorSWISetC (int argc __attribute__ ((unused)), portCHAR **argv) +{ + unsigned int state; + + if (!strcmp (argv [0], "on")) + state = 0; + else if (!strcmp (argv [0], "off")) + state = 1; + else + { + printf ("State must be 'on' or 'off'\n"); + return 0; + } + + printf ("Setting LED2 %s via C, previous state was %s\n", state ? "off" : "on", monitorSWICommon2 (SWICALL_C_LED2SET, state) ? "off" : "on"); + + return 0; +} + +int monitorSWIOnC (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("Setting LED2 on via C, previous state was %s\n", monitorSWICommon (SWICALL_C_LED2ON) ? "off" : "on"); + + return 0; +} + +int monitorSWIOffC (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("Setting LED2 off via C, previous state was %s\n", monitorSWICommon (SWICALL_C_LED2OFF) ? "off" : "on"); + + return 0; +} + +int monitorSWIToggleC (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("Toggling LED2 via C, previous state was %s\n", monitorSWICommon (SWICALL_C_LED2TOGGLE) ? "off" : "on"); + + return 0; +} + +// +// +// +int monitorWDTTest (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("The watchdog is disabled, by default. This command enables the watchdog, and if\n" + "no command is typed for 10 seconds, the watchdog will cause the system reset.\n" + "The 10 seconds is based on a 48Mhz PCLK (12Mhz xtal, PLL x 4, VBPDIV = /1). The\n" + "'wdt status' command will show the cause of a system reset. RSIR is reset by\n" + "this command.\n"); + + SCB_RSIR = SCB_RSIR_MASK; + WD_MOD = WD_MOD_WDEN | WD_MOD_RESET; + WD_TC = 120000000; + WD_FEED = WD_FEED_FEED1; + WD_FEED = WD_FEED_FEED2; + + return 0; +} + +int monitorWDTStatus (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + int regRSIR = SCB_RSIR; + int regWDMOD = WD_MOD; + int regWDTC = WD_TC; + int regWDTV = WD_TV; + + printf ("Watchdog enabled .................. : %s\n", (regWDMOD & WD_MOD_WDEN) ? "Yes" : "No"); + printf ("Watchdog timeout generates reset .. : %s\n", (regWDMOD & WD_MOD_RESET) ? "Yes" : "No"); + printf ("Watchdog timeout (in PCLK/4's) .... : %d\n", regWDTC); + printf ("Watchdog timeout counter .......... : %d\n", regWDTV); + printf ("\n"); + printf ("Reset because of POR .............. : %s\n", (regRSIR & SCB_RSIR_POR) ? "Yes" : "No"); + printf ("Reset because of /RESET ........... : %s\n", (regRSIR & SCB_RSIR_EXTR) ? "Yes" : "No"); + printf ("Reset because of watchdog ......... : %s\n", (regRSIR & SCB_RSIR_WDTR) ? "Yes" : "No"); + printf ("Reset because of BOD .............. : %s\n", (regRSIR & SCB_RSIR_BODR) ? "Yes" : "No"); + + return 0; +} + +int monitorWDTClear (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + SCB_RSIR = SCB_RSIR_MASK; + WD_MOD = 0; + + return 0; +} + +// +// +// +static int monitorVersion (int argc __attribute__ ((unused)), portCHAR **argv __attribute__ ((unused))) +{ + printf ("LPC-P2148 Demo, Version " __VERSION ", " __DATE__ " " __TIME__ "\n"); + printf ("Copyright (c) 2007, J.C. Wren\n"); + + return 0; +} + +// +// +// +#if defined CFG_CONSOLE_USB || defined CFG_CONSOLE_UART1 +static void monitorReassignFD (FILE *fp, int fd); +static void monitorReassignFD (FILE *fp, int fd) +{ + fp->_file = fd; +} +#endif + +static void argsDispatch (commandList_t *cl, int argc, char **argv) +{ + activeCommandList = cl; + + while (cl->command) + { + if (!strcmp (cl->command, argv [0])) + { + if ((argc == 2) && !strcmp (argv [1], "?")) + printf ("%s\n", cl->parameters); + else if ((argc - 1) < cl->minArgs) + printf ("Too few arguments to command (%d expected)\n", cl->minArgs); + else if (cl->cmdType == CMDTYPE_CMDLIST) + argsDispatch (cl->commandList, argc - 1, &argv [1]); + else if ((argc - 1) > cl->maxArgs) + printf ("Too many arguments to command (%d maximum)\n", cl->maxArgs); + else + (*cl->handler) (argc - 1, &argv [1]); + + return; + } + + cl++; + } + + if (!cl->command) + printf ("Unknown command \"%s\"\n", argv [0]); +} + +portTASK_FUNCTION (vMonitorTask, pvParameters __attribute__ ((unused))) +{ + static U8 buffer [256]; + static portCHAR *argv [34]; + int argc; + int fd = fileno (stdin); + + fclose (stderr); + stderr = stdout; + +#if defined CFG_CONSOLE_USB + fd = open ("/dev/usb", O_RDWR); +#elif defined CFG_CONSOLE_UART1 + fd = open ("/dev/uart1", O_RDWR); +#endif + +#if defined CFG_CONSOLE_USB || defined CFG_CONSOLE_UART1 + monitorReassignFD (stdin, fd); + monitorReassignFD (stdout, fd); +#endif + + monitorVersion (0, NULL); + +#ifndef CFG_USB_MSC + // + // If CFG_USB_MSC defined, this gets done by mscblockInit() + // + { + FRESULT f; + + if ((f = diskInitialize (0)) != FR_OK) + f_printerror (f); + } +#endif + + for (;;) + { + int l; + + if ((l = argsGetLine (fd, buffer, sizeof (buffer)))) + { + if ((l == 1) && ((buffer [0] == 0xfe) || (buffer [0] == 0xff))) + { + int type = buffer [0]; + time_t now = time (NULL); + ctime_r (&now, (char *) buffer); + printf ("%s -- %s", (type == 0xfe) ? "ALARM" : "PERIODIC", buffer); + } + else if (argsParse ((char *) buffer, argv, sizeof (argv), &argc)) + printf ("Too many arguments (max %ld)\n", arrsizeof (argv)); + else if (argv [0]) + argsDispatch (commandList, argc, &argv [0]); + } + + WD_FEED = WD_FEED_FEED1; + WD_FEED = WD_FEED_FEED2; + } +} diff --git a/monitor/monitor.h b/monitor/monitor.h new file mode 100644 index 0000000..0bcf3a5 --- /dev/null +++ b/monitor/monitor.h @@ -0,0 +1,6 @@ +#ifndef _MONITOR_H_ +#define _MONITOR_H_ + +portTASK_FUNCTION_PROTO (vMonitorTask, pvParameters __attribute__ ((unused))); + +#endif diff --git a/newlib/Makefile b/newlib/Makefile new file mode 100644 index 0000000..5a58c7d --- /dev/null +++ b/newlib/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=syscalls.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/newlib/syscalls.c b/newlib/syscalls.c new file mode 100644 index 0000000..a7d743d --- /dev/null +++ b/newlib/syscalls.c @@ -0,0 +1,732 @@ +// +// Support files for GNU libc. Files in the system namespace go here. +// Files in the C namespace (ie those that do not start with an underscore) go in .c +// +#include <_ansi.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "../uart/uart.h" +#include "../usbser/usbser.h" +#include "../rtc/rtc.h" +#include "../fatfs/ff.h" + +// +// Forward prototypes +// +int _mkdir _PARAMS ((const char *, mode_t)); +int _chmod _PARAMS ((const char *, mode_t)); +int _read _PARAMS ((int, char *, int)); +int _lseek _PARAMS ((int, int, int)); +int _write _PARAMS ((int, const char *, int)); +int _open _PARAMS ((const char *, int, ...)); +int _close _PARAMS ((int)); +int _kill _PARAMS ((int, int)); +void _exit _PARAMS ((int)); +int _getpid _PARAMS ((int)); +caddr_t _sbrk _PARAMS ((int)); +int _fstat _PARAMS ((int, struct stat *)); +int _stat _PARAMS ((const char *, struct stat *)); +int _link _PARAMS ((void)); +int _unlink _PARAMS ((const char *)); +void _raise _PARAMS ((void)); +int _gettimeofday _PARAMS ((struct timeval *, struct timezone *)); +clock_t _times _PARAMS ((struct tms *)); +int isatty _PARAMS ((int)); +int _rename _PARAMS ((const char *, const char *)); +int _system _PARAMS ((const char *)); +void _sync _PARAMS ((void)); +void syscallsInit _PARAMS ((void)); + +int rename _PARAMS ((const char *, const char *)); + +static int set_errno _PARAMS ((int)); +static time_t fatfs_time_to_timet _PARAMS ((FILINFO *)); +static int remap_handle _PARAMS ((int)); +static int findslot _PARAMS ((int)); + +// +// Register name faking - works in collusion with the linker +// +register char *stack_ptr asm ("sp"); + +// +// Following is copied from libc/stdio/local.h to check std streams +// +extern void _EXFUN (__sinit,( struct _reent *)); + +#define CHECK_INIT(ptr) \ +do \ +{ \ + if ((ptr) && !(ptr)->__sdidinit) \ + __sinit (ptr); \ +} \ +while (0) + +// +// Adjust our internal handles to stay away from std* handles +// +#define FILE_HANDLE_OFFSET (0x20) + +#define MONITOR_STDIN 0 +#define MONITOR_STDOUT 1 +#define MONITOR_STDERR 2 +#define MONITOR_UART0 3 +#define MONITOR_UART1 4 +#define MONITOR_USB 5 +#define MONITOR_FATFS 6 + +// +// +// +typedef struct +{ + int handle; + int pos; + int flags; + FIL *fatfsFCB; +} +openFiles_t; + +// +// +// +#define MAX_OPEN_FILES 10 +static openFiles_t openfiles [MAX_OPEN_FILES]; + +static int findslot (int fh) +{ + static int slot; + static int lastfh = -1; + + if ((fh != -1) && (fh == lastfh)) + return slot; + + for (slot = 0; slot < MAX_OPEN_FILES; slot++) + if (openfiles [slot].handle == fh) + break; + + lastfh = fh; + + return slot; +} + +// +// Function to convert std(in|out|err) handles to internal versions. +// +static int remap_handle (int fh) +{ + CHECK_INIT(_REENT); + + if (fh == STDIN_FILENO) + return MONITOR_STDIN; + if (fh == STDOUT_FILENO) + return MONITOR_STDOUT; + if (fh == STDERR_FILENO) + return MONITOR_STDERR; + + return fh - FILE_HANDLE_OFFSET; +} + +static int remap_fatfs_errors (FRESULT f) +{ + switch (f) + { + case FR_NO_FILE : errno = ENOENT; break; + case FR_NO_PATH : errno = ENOENT; break; + case FR_INVALID_NAME : errno = EINVAL; break; + case FR_INVALID_DRIVE : errno = ENODEV; break; + case FR_DENIED : errno = EACCES; break; + case FR_EXIST : errno = EEXIST; break; + case FR_NOT_READY : errno = EIO; break; + case FR_WRITE_PROTECTED : errno = EACCES; break; + case FR_RW_ERROR : errno = EIO; break; + case FR_NOT_ENABLED : errno = EIO; break; + case FR_NO_FILESYSTEM : errno = EIO; break; + case FR_INVALID_OBJECT : errno = EBADF; break; + default : errno = EIO; break; + } + + return -1; +} + +static time_t fatfs_time_to_timet (FILINFO *f) +{ + struct tm tm; + + tm.tm_sec = (f->ftime & 0x001f) << 1; + tm.tm_min = (f->ftime & 0x07e0) >> 5; + tm.tm_hour = (f->ftime & 0xf800) >> 11; + tm.tm_mday = (f->fdate & 0x001f); + tm.tm_mon = ((f->fdate & 0x01e0) >> 5) - 1; + tm.tm_year = ((f->fdate & 0xfe00) >> 9) + 80; + tm.tm_isdst = 0; + + return mktime (&tm); +} + +static int set_errno (int errval) +{ + errno = errval; + + return -1; +} + +void syscallsInit (void) +{ + int slot; + static int initialized = 0; + + if (initialized) + return; + + initialized = 1; + + __builtin_memset (openfiles, 0, sizeof (openfiles)); + + for (slot = 0; slot < MAX_OPEN_FILES; slot++) + openfiles [slot].handle = -1; + + openfiles [0].handle = MONITOR_STDIN; + openfiles [1].handle = MONITOR_STDOUT; + openfiles [2].handle = MONITOR_STDERR; +} + +int _mkdir (const char *path, mode_t mode __attribute__ ((unused))) +{ + FRESULT f; + + if ((f = f_mkdir (path)) != FR_OK) + return remap_fatfs_errors (f); + + return 0; +} + +int _chmod (const char *path, mode_t mode) +{ + FRESULT f; + + if ((f = f_chmod (path, (mode & S_IWUSR) ? 0 : AM_RDO, AM_RDO)) != FR_OK) + return remap_fatfs_errors (f); + + return 0; +} + +int _read (int fd, char *ptr, int len) +{ + int i; + int fh; + int slot; + portTickType xBlockTime; + int bytesUnRead = -1; + + if ((slot = findslot (fh = remap_handle (fd))) == MAX_OPEN_FILES) + return set_errno (EBADF); + + if (openfiles [slot].flags & O_WRONLY) + return set_errno (EBADF); + + xBlockTime = (openfiles [slot].flags & O_NONBLOCK) ? 0 : portMAX_DELAY; + + switch (fh) + { + case MONITOR_STDIN : + { + for (i = 0; i < len; i++) + if (!uartGetChar (0, (signed portCHAR *) ptr++, xBlockTime)) + break; + + bytesUnRead = len - i; + } + break; + + case MONITOR_STDOUT : + case MONITOR_STDERR : + break; + + case MONITOR_UART0 : + { + for (i = 0; i < len; i++) + if (!uartGetChar (0, (signed portCHAR *) ptr++, xBlockTime)) + break; + + bytesUnRead = len - i; + } + break; + + case MONITOR_UART1 : + { + for (i = 0; i < len; i++) + if (!uartGetChar (1, (signed portCHAR *) ptr++, xBlockTime)) + break; + + bytesUnRead = len - i; + } + break; + + case MONITOR_USB : + { + for (i = 0; i < len; i++) + if (!usbserGetChar ((signed portCHAR *) ptr++, xBlockTime)) + break; + + bytesUnRead = len - i; + } + break; + + default : + { + if (openfiles [slot].fatfsFCB) + { + FRESULT f; + U16 fatfsBytesRead; + + if ((f = f_read (openfiles [slot].fatfsFCB, ptr, len, &fatfsBytesRead)) != FR_OK) + return remap_fatfs_errors (f); + + bytesUnRead = len - fatfsBytesRead; + } + } + break; + } + + if (bytesUnRead < 0) + return -1; + + openfiles [slot].pos += len - bytesUnRead; + + return len - bytesUnRead; +} + +int _lseek (int fd, int ptr, int dir) +{ + int fh; + int slot; + FRESULT f = FR_INVALID_OBJECT; + + if (((slot = findslot (fh = remap_handle (fd))) == MAX_OPEN_FILES) || !openfiles [slot].fatfsFCB) + return set_errno (EBADF); + + if (dir == SEEK_SET) + f = f_lseek (openfiles [slot].fatfsFCB, ptr); + else if (dir == SEEK_CUR) + f = f_lseek (openfiles [slot].fatfsFCB, openfiles [slot].fatfsFCB->fptr + ptr); + else if (dir == SEEK_END) + f = f_lseek (openfiles [slot].fatfsFCB, openfiles [slot].fatfsFCB->fsize + ptr); + + if (f != FR_OK) + return remap_fatfs_errors (f); + + return openfiles [slot].pos = openfiles [slot].fatfsFCB->fptr; +} + +int _write (int fd, const char *ptr, int len) +{ + int i; + int fh; + int slot; + portTickType xBlockTime; + int bytesUnWritten = -1; + + if ((slot = findslot (fh = remap_handle (fd))) == MAX_OPEN_FILES) + return set_errno (EBADF); + + if (openfiles [slot].flags & O_RDONLY) + return set_errno (EBADF); + + xBlockTime = (openfiles [slot].flags & O_NONBLOCK) ? 0 : portMAX_DELAY; + + switch (fh) + { + case MONITOR_STDIN : + break; + + case MONITOR_STDOUT : + case MONITOR_STDERR : + { + for (i = 0; i < len; i++) + { + if (*ptr == '\n') + if (!uartPutChar (0, '\r', xBlockTime)) + break; + if (!uartPutChar (0, *ptr++, xBlockTime)) + break; + } + + bytesUnWritten = len - i; + } + break; + + case MONITOR_UART0 : + { + for (i = 0; i < len; i++) + if (!uartPutChar (0, *ptr++, xBlockTime)) + break; + + bytesUnWritten = len - i; + } + break; + + case MONITOR_UART1 : + { + for (i = 0; i < len; i++) + { +#ifdef CFG_CONSOLE_UART1 + if (*ptr == '\n') + if (!uartPutChar (1, '\r', xBlockTime)) + break; +#endif + if (!uartPutChar (1, *ptr++, xBlockTime)) + break; + } + + bytesUnWritten = len - i; + } + break; + + case MONITOR_USB : + { + for (i = 0; i < len; i++) + { +#ifdef CFG_CONSOLE_USB + if (*ptr == '\n') + if (!usbserPutChar ('\r', xBlockTime)) + break; +#endif + if (!usbserPutChar (*ptr++, xBlockTime)) + break; + } + + bytesUnWritten = len - i; + } + break; + + default : + { + if (openfiles [slot].fatfsFCB) + { + FRESULT f; + U16 fatfsBytesWritten; + + if ((f = f_write (openfiles [slot].fatfsFCB, ptr, len, &fatfsBytesWritten)) != FR_OK) + return remap_fatfs_errors (f); + + bytesUnWritten = len - fatfsBytesWritten; + } + else + return set_errno (EBADF); + } + break; + } + + if (bytesUnWritten == -1 || bytesUnWritten == len) + return -1; + + openfiles [slot].pos += len - bytesUnWritten; + + return len - bytesUnWritten; +} + +int _open (const char *path, int flags, ...) +{ + int fh = 0; + int slot; + + if ((slot = findslot (-1)) == MAX_OPEN_FILES) + return set_errno (ENFILE); + + if (flags & O_APPEND) + flags &= ~O_TRUNC; + + if (!__builtin_strcmp (path, "/dev/uart0")) + fh = MONITOR_UART0; + else if (!__builtin_strcmp (path, "/dev/uart1")) + fh = MONITOR_UART1; + else if (!__builtin_strcmp (path, "/dev/usb")) + fh = MONITOR_USB; + else + { + U8 fatfsFlags = FA_OPEN_EXISTING; + FRESULT f; + + // + // FA_OPEN_EXISTING Opens the file. The function fails if the file is not existing. (Default) + // FA_OPEN_ALWAYS Opens the file, if it is existing. If not, the function creates the new file. + // FA_CREATE_NEW Creates a new file. The function fails if the file is already existing. + // FA_CREATE_ALWAYS Creates a new file. If the file is existing, it is truncated and overwritten. + // + // O_CREAT If the file does not exist it will be created. + // O_EXCL When used with O_CREAT, if the file already exists it is an error and the open() will fail. + // O_TRUNC If the file already exists and is a regular file and the open mode allows writing (i.e., is O_RDWR or O_WRONLY) it will be truncated to length 0. + // + if (((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) && (flags & (O_RDWR | O_WRONLY))) + fatfsFlags = FA_CREATE_ALWAYS; + else if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + fatfsFlags = FA_OPEN_EXISTING; + else if ((flags & O_CREAT) == O_CREAT) + fatfsFlags = FA_OPEN_ALWAYS; + else if ((flags == O_RDONLY) || (flags == O_WRONLY) || (flags == O_RDWR)) + fatfsFlags = FA_OPEN_EXISTING; + else + return set_errno (EINVAL); + + if ((flags & O_ACCMODE) == O_RDONLY) + fatfsFlags |= FA_READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + fatfsFlags |= FA_WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) + fatfsFlags |= (FA_READ | FA_WRITE); + else + return set_errno (EINVAL); + + fh = -1; + errno = EIO; + + if (!openfiles [slot].fatfsFCB) + if ((openfiles [slot].fatfsFCB = __builtin_calloc (1, sizeof (FIL)))) + if ((fh = ((f = f_open (openfiles [slot].fatfsFCB, path, fatfsFlags)) == FR_OK) ? (slot + MONITOR_FATFS) : -1) == -1) + return remap_fatfs_errors (f); + } + + if (fh >= 0) + { + openfiles [slot].handle = fh; + openfiles [slot].pos = 0; + openfiles [slot].flags = flags; + } + + return fh >= 0 ? (fh + FILE_HANDLE_OFFSET) : -1; +} + +int _close (int fd) +{ + int slot; + + if ((slot = findslot (remap_handle (fd))) == MAX_OPEN_FILES) + return set_errno (EBADF); + + openfiles [slot].handle = -1; + + if (openfiles [slot].fatfsFCB) + { + FRESULT f; + + f = f_close (openfiles [slot].fatfsFCB); + free (openfiles [slot].fatfsFCB); + openfiles [slot].fatfsFCB = NULL; + + if (f != FR_OK) + return remap_fatfs_errors (f); + } + + return 0; +} + +int _kill (int pid __attribute__ ((unused)), int sig __attribute__ ((unused))) +{ + return set_errno (ENOTSUP); +} + +void _exit (int status) +{ + /* There is only one SWI for both _exit and _kill. For _exit, call + the SWI with the second argument set to -1, an invalid value for + signum, so that the SWI handler can distinguish the two calls. + Note: The RDI implementation of _kill throws away both its + arguments. */ + _kill (status, -1); + + while (1); +} + +int _getpid (int n) +{ + return 1; + n = n; +} + +caddr_t _sbrk (int incr) +{ + extern char end asm ("end"); /* Defined by the linker. */ + extern unsigned long __heap_max; + static char *heap_end; + char *prev_heap_end; + char *eom; + + if (!heap_end) + heap_end = & end; + + prev_heap_end = heap_end; + + // + // If FreeRTOS is not running, the stack pointer will be in the SVC region, + // and therefore greater than the start of the heap (end of .bss). In this + // case, we see if the heap allocation will run past the current stack + // pointer, and if so, return ENOMEM (this does not guarantee subsequent + // stack operations won't overflow into the heap!). + // + // If FreeRTOS is running, then we define the end of the heap to be the + // start of end of the supervisor stack (since FreeRTOS is running, the + // system stack, and very little of the supervisor stack space is used). + // + eom = (stack_ptr > & end) ? stack_ptr : (char *) __heap_max; + + if (heap_end + incr > eom) + { + errno = ENOMEM; + return (caddr_t) -1; + } + + heap_end += incr; + + return (caddr_t) prev_heap_end; +} + +// +// Tihs is a problem. FatFS has no way to go from a file handle back to a file, +// since file name information isn't stored with the handle. Tried to think of +// a good way to handle this, couldn't come up with anything. For now, it +// returns ENOSYS (not implemented). +// +int _fstat (int fd __attribute__ ((unused)), struct stat *st __attribute__ ((unused))) +{ + return set_errno (ENOSYS); +} + +int _stat (const char *fname, struct stat *st) +{ + FRESULT f; + FILINFO fi; + + if ((f = f_stat (fname, &fi)) != FR_OK) + return remap_fatfs_errors (f); + + __builtin_memset (st, 0, sizeof (* st)); + + st->st_mode |= (fi.fattrib & AM_DIR) ? S_IFDIR : S_IFREG; + st->st_mode |= (fi.fattrib & AM_RDO) ? ((S_IRWXU & ~S_IWUSR) | (S_IRWXG & ~S_IWGRP) | (S_IRWXO & ~S_IWOTH)) : (S_IRWXU | S_IRWXG | S_IRWXO); + st->st_size = fi.fsize; + st->st_ctime = fatfs_time_to_timet (&fi); + st->st_mtime = st->st_ctime; + st->st_atime = st->st_ctime; + st->st_blksize = 512; + + return 0; +} + +// +// FatFS and FAT file systems don't support links +// +int _link (void) +{ + return set_errno (ENOSYS); +} + +int _unlink (const char *path) +{ + FRESULT f; + + if ((f = f_unlink (path)) != FR_OK) + return remap_fatfs_errors (f); + + return 0; +} + +void _raise (void) +{ + return; +} + +int _gettimeofday (struct timeval *tp, struct timezone *tzp) +{ + unsigned int milliseconds; + + if (tp) + { + tp->tv_sec = rtcGetEpochSeconds (&milliseconds); + tp->tv_usec = milliseconds * 1000; + } + + // + // Return fixed data for the timezone + // + if (tzp) + { + tzp->tz_minuteswest = 0; + tzp->tz_dsttime = 0; + } + + return 0; +} + +// +// Return a clock that ticks at 100Hz +// +clock_t _times (struct tms *tp) +{ + clock_t timeval = (clock_t) xTaskGetTickCount (); + + if (tp) + { + tp->tms_utime = timeval; + tp->tms_stime = 0; + tp->tms_cutime = 0; + tp->tms_cstime = 0; + } + + return timeval; +}; + +int isatty (int fd) +{ + return (fd <= 2) ? 1 : 0; /* one of stdin, stdout, stderr */ +} + +int _system (const char *s) +{ + if (s == NULL) + return 0; + + return set_errno (ENOSYS); +} + +int _rename (const char *oldpath, const char *newpath) +{ + FRESULT f; + + if ((f = f_rename (oldpath, newpath))) + return remap_fatfs_errors (f); + + return 0; +} + +// +// Default crossdev -t options for ARM newlib doesn't define HAVE_RENAME, so +// it's trying to use the link/unlink process. FatFS doesn't support link(), +// so override the newlib rename() to make it work correctly. +// +int rename (const char *oldpath, const char *newpath) +{ + return _rename (oldpath, newpath); +} + +void _sync (void) +{ + int slot; + + for (slot = 0; slot < MAX_OPEN_FILES; slot++) + if (openfiles [slot].fatfsFCB) + f_sync (openfiles [slot].fatfsFCB); +} diff --git a/rtc/Makefile b/rtc/Makefile new file mode 100644 index 0000000..bfd113d --- /dev/null +++ b/rtc/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=rtc.c rtcISR.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/rtc/rtc.c b/rtc/rtc.c new file mode 100644 index 0000000..6ae6337 --- /dev/null +++ b/rtc/rtc.c @@ -0,0 +1,268 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include +#include +#include +#include + +#include "rtcISR.h" +#include "rtc.h" + +// +// Place RTC on 32kHz xtal and disconnect power. +// +static inline void rtcSleep (void) +{ + RTC_CCR = (RTC_CCR_CLKEN | RTC_CCR_CLKSRC); + SCB_PCONP &= ~SCB_PCONP_PCRTC; +} + +// +// Prepare clock for interactive use. +// +static inline void rtcWake (void) +{ + RTC_CCR = (RTC_CCR_CLKEN | RTC_CCR_CLKSRC); + SCB_PCONP |= SCB_PCONP_PCRTC; +} + +// +// Read clock registers and return tm structure. +// +static void rtcRead (struct tm *theTime, unsigned int *milliseconds) +{ + unsigned int ticks32Khz; + rtcCTIME0_t ctime0; + rtcCTIME1_t ctime1; + rtcCTIME2_t ctime2; + + rtcWake (); + portENTER_CRITICAL (); + + do + { + ctime0.i = RTC_CTIME0; + ctime1.i = RTC_CTIME1; + ctime2.i = RTC_CTIME2; + + do + ticks32Khz = (RTC_CTC & 0xfffe); + while (ticks32Khz != (RTC_CTC & 0xfffe)); + } + while (ctime0.i != RTC_CTIME0); + + portEXIT_CRITICAL (); + rtcSleep (); + + if (milliseconds) + *milliseconds = (((ticks32Khz / 2) * 1000u) / 32768u); + + theTime->tm_sec = ctime0.seconds; + theTime->tm_min = ctime0.minutes; + theTime->tm_hour = ctime0.hours; + theTime->tm_mday = ctime1.dom; + theTime->tm_mon = ctime1.month - 1; + theTime->tm_year = ctime1.year - 1900; + theTime->tm_wday = ctime0.dow; + theTime->tm_yday = ctime2.doy - 1; + theTime->tm_isdst = 0; +} + +// +// Set clock to new values. +// +static void rtcWrite (struct tm *newTime) +{ + rtcWake (); + portENTER_CRITICAL (); + + RTC_CCR &= ~RTC_CCR_CLKEN; + RTC_CCR |= RTC_CCR_CTCRST; + + RTC_SEC = newTime->tm_sec; + RTC_MIN = newTime->tm_min; + RTC_HOUR = newTime->tm_hour; + RTC_DOM = newTime->tm_mday; + RTC_MONTH = newTime->tm_mon + 1; + RTC_YEAR = newTime->tm_year + 1900; + RTC_DOW = newTime->tm_wday; + RTC_DOY = newTime->tm_yday + 1; + + RTC_CCR &= ~RTC_CCR_CTCRST; + RTC_CCR |= RTC_CCR_CLKEN; + + portEXIT_CRITICAL (); + rtcSleep (); +} + +// +// +// +time_t rtcGetEpochSeconds (unsigned int *milliseconds) +{ + struct tm tm; + + rtcRead (&tm, milliseconds); + return mktime (&tm); +} + +void rtcSetEpochSeconds (time_t now) +{ + struct tm tm; + + localtime_r (&now, &tm); + rtcWrite (&tm); +} + +// +// Start clock so that the sytsem may use it. +// +static void rtcInitClockCalendar (void) +{ + int nonsense = 0; + rtcCTIME0_t ctime0; + rtcCTIME1_t ctime1; + rtcCTIME2_t ctime2; + + rtcWake (); + + ctime0.i = RTC_CTIME0; + ctime1.i = RTC_CTIME1; + ctime2.i = RTC_CTIME2; + + // + // Leisurely tear the packed time apart into individual time. + // + if ((ctime0.seconds < 0) || (ctime0.seconds > 59)) nonsense = 1; + if ((ctime0.minutes < 0) || (ctime0.minutes > 59)) nonsense = 1; + if ((ctime0.hours < 0) || (ctime0.hours > 23)) nonsense = 1; + if ((ctime1.dom < 1) || (ctime1.dom > 31)) nonsense = 1; + if ((ctime1.month < 1) || (ctime1.month > 12)) nonsense = 1; + if ((ctime1.year < 1980) || (ctime1.year > 2050)) nonsense = 1; + if ((ctime0.dow < 0) || (ctime0.dow > 6)) nonsense = 1; + if ((ctime2.doy < 0) || (ctime2.doy > 366)) nonsense = 1; + + rtcSleep (); + + // + // Set the clock to Jan 1, 2006 00:00:00 + // + if (nonsense) + rtcSetEpochSeconds ((time_t) 1136073600); +} + +// +// +// +void rtcInit (void) +{ + rtcISRInit (); + + SCB_PCONP |= SCB_PCONP_PCRTC; + + RTC_CCR = 0; + + RTC_CCR |= RTC_CCR_CLKSRC; + + RTC_AMR = RTC_AMR_AMRMASK; + RTC_CIIR = 0; + + RTC_ILR = RTC_ILR_MASK; + + RTC_CCR |= RTC_CCR_CLKEN; + + VIC_IntSelect &= ~VIC_IntSelect_RTC; +#ifdef RTC_NONVECTOREDIRQ + VIC_DefVectAddr = (portLONG) rtcISR; +#else + VIC_VectAddr6 = (portLONG) rtcISR; + VIC_VectCntl6 = VIC_VectCntl_ENABLE | VIC_Channel_RTC; +#endif + VIC_IntEnable = VIC_IntEnable_RTC; + + rtcInitClockCalendar (); +} + +int rtcSetAlarm (struct tm *tm) +{ + if (tm && (mktime (tm) < time (NULL))) + return -1; + + rtcWake (); + + RTC_AMR = RTC_AMR_AMRMASK; + + if (tm) + { + RTC_ALYEAR = tm->tm_year + 1900; + RTC_ALMON = tm->tm_mon + 1; + RTC_ALDOM = tm->tm_mday; + RTC_ALHOUR = tm->tm_hour; + RTC_ALMIN = tm->tm_min; + RTC_ALSEC = tm->tm_sec; + RTC_ALDOW = 0; + RTC_ALDOY = 0; + + RTC_AMR = RTC_AMR_AMRDOW | RTC_AMR_AMRDOY; + } + + rtcSleep (); + + return 0; +} + +struct tm *rtcGetAlarmTm (struct tm *tm) +{ + if (tm) + { + memset (tm, 0, sizeof (* tm)); + + rtcWake (); + + if (RTC_AMR != RTC_AMR_AMRMASK) + { + tm->tm_year = RTC_ALYEAR - 1900; + tm->tm_mon = RTC_ALMON - 1; + tm->tm_mday = RTC_ALDOM; + tm->tm_hour = RTC_ALHOUR; + tm->tm_min = RTC_ALMIN; + tm->tm_sec = RTC_ALSEC; + } + else + { + time_t now = 0; + memcpy (tm, localtime (&now), sizeof (* tm)); + } + + rtcSleep (); + } + + return tm; +} + +time_t rtcGetAlarmEpochSeconds (void) +{ + struct tm tm; + + return mktime (rtcGetAlarmTm (&tm)); +} + +int rtcPeriodicAlarm (int mode) +{ + int state; + + rtcWake (); + + state = RTC_CIIR & RTC_CIIR_IMMIN; + + if (!mode) + RTC_CIIR &= ~RTC_CIIR_IMMIN; + else if (mode > 0) + RTC_CIIR |= RTC_CIIR_IMMIN; + + rtcSleep (); + + return state ? 1 : 0; +} diff --git a/rtc/rtc.h b/rtc/rtc.h new file mode 100644 index 0000000..1564a3c --- /dev/null +++ b/rtc/rtc.h @@ -0,0 +1,23 @@ +#ifndef _RTC_H_ +#define _RTC_H_ + +#include + +// +// Define this for using non-vectored IRQs. Undef it if a regular vectored +// IRQ is preferred. +// +#define RTC_NONVECTOREDIRQ + +// +// +// +void rtcInit (void); +time_t rtcGetEpochSeconds (unsigned int *milliseconds); +void rtcSetEpochSeconds (time_t now); +int rtcSetAlarm (struct tm *tm); +struct tm *rtcGetAlarmTm (struct tm *tm); +time_t rtcGetAlarmEpochSeconds (void); +int rtcPeriodicAlarm (int mode); + +#endif diff --git a/rtc/rtcISR.c b/rtc/rtcISR.c new file mode 100644 index 0000000..a4a52e4 --- /dev/null +++ b/rtc/rtcISR.c @@ -0,0 +1,75 @@ +#include + +// +// Scheduler includes +// +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "../uart/uart.h" +#include "../usbser/usbser.h" +#include "rtcISR.h" +#include "rtc.h" + +// +// +// +static xQueueHandle consoleQueue = NULL; + +// +// +// +void rtcISRInit (void) +{ +#if defined CFG_CONSOLE_USB + usbserGetRxQueue (&consoleQueue); +#elif defined CFG_CONSOLE_UART0 + uart0GetRxQueue (&consoleQueue); +#elif defined CFG_CONSOLE_UART1 + uart1GetRxQueue (&consoleQueue); +#else +#error "Eeek! No console devices defined!" +#endif +} + +// +// +// +void rtcISR (void) __attribute__ ((naked)); +void rtcISR (void) +{ + portENTER_SWITCHING_ISR (); + + portBASE_TYPE taskWoken = pdFALSE; + + RTC_CCR = (RTC_CCR_CLKEN | RTC_CCR_CLKSRC); + SCB_PCONP |= SCB_PCONP_PCRTC; + + if (RTC_ILR & RTC_ILR_RTCCIF) + { + U8 c = 0xff; + + if (consoleQueue && xQueueSendFromISR (consoleQueue, &c, (portBASE_TYPE) pdFALSE)) + taskWoken = pdTRUE; + + RTC_ILR = RTC_ILR_RTCCIF; + } + + if (RTC_ILR & RTC_ILR_RTCALF) + { + U8 c = 0xfe; + + if (consoleQueue && xQueueSendFromISR (consoleQueue, &c, (portBASE_TYPE) pdFALSE)) + taskWoken = pdTRUE; + + RTC_ILR = RTC_ILR_RTCALF; + } + + VIC_VectAddr = (unsigned portLONG) 0; + + RTC_CCR = (RTC_CCR_CLKEN | RTC_CCR_CLKSRC); + SCB_PCONP &= ~SCB_PCONP_PCRTC; + + portEXIT_SWITCHING_ISR (taskWoken); +} diff --git a/rtc/rtcISR.h b/rtc/rtcISR.h new file mode 100644 index 0000000..26eb6a1 --- /dev/null +++ b/rtc/rtcISR.h @@ -0,0 +1,8 @@ +#ifndef _RTCISR_H_ +#define _RTCISR_H_ + +void rtcISRInit (void); +void rtcISR (void); + +#endif + diff --git a/sensors/Makefile b/sensors/Makefile new file mode 100644 index 0000000..ba31b01 --- /dev/null +++ b/sensors/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=sensors.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/sensors/sensors.c b/sensors/sensors.c new file mode 100644 index 0000000..86fb39c --- /dev/null +++ b/sensors/sensors.c @@ -0,0 +1,94 @@ +// +// +// +#include +#include +#include +#include + +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +#include "../main.h" +#include "../adc/adc.h" +#include "../dac/dac.h" +#include "../leds/leds.h" +#include "sensors.h" + +// +// +// +static sensorData_t sensorData; +static xSemaphoreHandle semaphore; + +// +// Return 1 if got a copy, 0 if not. +// +int sensorsCopyData (sensorData_t *dst) +{ + if (xSemaphoreTake (semaphore, 100 / portTICK_RATE_MS) == pdTRUE) + { + memcpy (dst, &sensorData, sizeof (sensorData)); + xSemaphoreGive (semaphore); + return 1; + } + + memset (dst, 0, sizeof (sensorData_t)); + return 0; +} + +// +// +// +portTASK_FUNCTION (vSensorsTask, pvParameters __attribute__ ((unused))) +{ + portTickType xTickCount; + int adcValue; + int adcLastValue = -1; + int adcChanged = -1; + int dacValue = 0; + int dacDirection = -16; + + memset (&sensorData, 0, sizeof (sensorData)); + + vSemaphoreCreateBinary (semaphore); + + xTickCount = xTaskGetTickCount (); + + for (;;) + { + vTaskDelayUntil (&xTickCount, 100 / portTICK_RATE_MS); + + // + // Adjust the DAC value so we output a slow sine wave + // + if ((dacValue <= 0) || (dacValue >= (1024 - dacDirection))) + dacDirection = 0 - dacDirection; + + dacSet (dacValue); + dacValue += dacDirection; + + // + // Read the current ADC value, keep only top 2 bits. If it changes, + // tell the LED task to change the blink rate. + // + if ((adcValue = (adcRead0_3 () >> 8)) != adcLastValue) + { + xQueueSend (xLEDQueue, &adcValue, (portTickType) 0); + adcLastValue = adcValue; + adcChanged++; + } + + // + // Update the sensors data + // + if (xSemaphoreTake (semaphore, portMAX_DELAY) == pdTRUE) + { + sensorData.sensorCount++; + sensorData.adcChanges = adcChanged; + + xSemaphoreGive (semaphore); + } + } +} diff --git a/sensors/sensors.h b/sensors/sensors.h new file mode 100644 index 0000000..380e62e --- /dev/null +++ b/sensors/sensors.h @@ -0,0 +1,22 @@ +#ifndef _SENSORS_H_ +#define _SENSORS_H_ + +#include "semphr.h" + +// +// +// +typedef struct sensorData_s +{ + int sensorCount; + int adcChanges; +} +sensorData_t; + +// +// +// +int sensorsCopyData (sensorData_t *dst); +portTASK_FUNCTION_PROTO (vSensorsTask, pvParameters __attribute__ ((unused))); + +#endif diff --git a/swi/Makefile b/swi/Makefile new file mode 100644 index 0000000..7f716f8 --- /dev/null +++ b/swi/Makefile @@ -0,0 +1,31 @@ +SRC_FILES=swi.c +ASMSRC_FILES=swidispatch.s + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) +ARM_ASMOBJ = $(ASMSRC_FILES:.s=.o) + +.PHONY: all +all: $(ARM_OBJ) $(ARM_ASMOBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +$(ARM_ASMOBJ) : %.o : %.s Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/swi/swi.c b/swi/swi.c new file mode 100644 index 0000000..b6aa66c --- /dev/null +++ b/swi/swi.c @@ -0,0 +1,73 @@ +#include "FreeRTOS.h" + +#include + +#include "swi.h" + +// +// +// +#define LED2 GPIO_IO_P11 + +// +// +// +void swiInit (void) +{ +} + +int swiDispatchC (unsigned long r0, unsigned long r1 __attribute__ ((unused)), unsigned long r2 __attribute__ ((unused)), unsigned long swi) +{ + int state = 0; + + switch (swi) + { + case SWICALL_YIELDPROCESSOR : + case SWICALL_A_LED2SET : + case SWICALL_A_LED2ON : + case SWICALL_A_LED2OFF : + case SWICALL_A_LED2TOGGLE : + printf ("Eeek! This should have been handled in the asm code\n"); + break; + + case SWICALL_C_LED2SET : + { + state = GPIO0_IOPIN & LED2; + + if (r0) + GPIO0_IOSET = LED2; + else + GPIO0_IOCLR = LED2; + } + break; + + case SWICALL_C_LED2ON : + { + state = GPIO0_IOPIN & LED2; + GPIO0_IOCLR = LED2; + } + break; + + case SWICALL_C_LED2OFF : + { + state = GPIO0_IOPIN & LED2; + GPIO0_IOSET = LED2; + } + break; + + case SWICALL_C_LED2TOGGLE : + { + if ((state = GPIO0_IOPIN & LED2)) + GPIO0_IOCLR = LED2; + else + GPIO0_IOSET = LED2; + } + break; + + default : + printf ("Eeek! Unhandled SWI value\n"); + break; + } + + return state; +} diff --git a/swi/swi.h b/swi/swi.h new file mode 100644 index 0000000..78d22ac --- /dev/null +++ b/swi/swi.h @@ -0,0 +1,60 @@ +#ifndef _SWI_H_ +#define _SWI_H_ + +// +// +// +typedef enum +{ + SWICALL_YIELDPROCESSOR = 0, // FreeRTOS xYieldTask (asm) + SWICALL_A_LED2SET, // Set LED2 on or off, return previous state (asm) + SWICALL_A_LED2ON, // LED2 on, return previous state (asm) + SWICALL_A_LED2OFF, // LED2 off, return previous state (asm) + SWICALL_A_LED2TOGGLE, // LED2 toggle, return previous state (asm) + SWICALL_C_LED2SET, // Set LED2 on or off, return previous state (C) + SWICALL_C_LED2ON, // LED2 on, return previous state (C) + SWICALL_C_LED2OFF, // LED2 off, return previous state (C) + SWICALL_C_LED2TOGGLE, // LED2 toggle, return previous state (C) +} +swiCalls_e; + +// +// +// +#define SWICALL(swiID, swiOut) \ + asm volatile (\ + "swi %a1 \n\t" \ + "mov %0,r0 \n\t" \ + : "=r" (swiOut) : "I" (swiID) : "r0", "lr") + +#define SWICALL1(swiID, swiIn, swiOut) \ + asm volatile (\ + "mov r0,%1 \t\n" \ + "swi %a2 \n\t" \ + "mov %0,r0 \n\t" \ + : "=r" (swiOut) : "r" (swiIn), "I" (swiID) : "r0", "lr") + +#define SWICALL2(swiID, swiIn1, swiIn2, swiOut) \ + asm volatile ( \ + "mov r0,%1 \t\n" \ + "mov r1,%2 \t\n" \ + "swi %a3 \n\t" \ + "mov %0,r0 \n\t" \ + : "=r" (swiOut) : "r" (swiIn1), "r" (swiIn2), "I" (swiID) : "r0", "lr") + +#define SWICALL3(swiID, swiIn1, swiIn2, swiIn3, swiOut) \ + asm volatile ( \ + "mov r0,%1 \t\n" \ + "mov r1,%2 \t\n" \ + "mov r2,%3 \t\n" \ + "swi %a4 \n\t" \ + "mov %0,r0 \n\t" \ + : "=r" (swiOut) : "r" (swiIn1), "r" (swiIn2), "r" (swiIn3), "I" (swiID) : "r0", "lr") + +// +// +// +void swiInit (void); +int swiDispatchC (unsigned long r0, unsigned long r1, unsigned long r2, unsigned long swi); + +#endif diff --git a/swi/swidispatch.s b/swi/swidispatch.s new file mode 100644 index 0000000..3b03c18 --- /dev/null +++ b/swi/swidispatch.s @@ -0,0 +1,131 @@ +@ +@ This code expects to find a function named 'swiDispatchC' (which should be +@ in swi.c) to dispatch any functions that aren't written in assembly. +@ (What nut-case picked '@' as the comment character for .s files?) +@ + .text + .extern swiDispatchC + .global swiDispatch + .func swiDispatch + +@ +@ Equates for GPIO0 registers +@ + .set GPIO0_IOPIN, 0xe0028000 + .set GPIO0_IOSET, 0xe0028004 + .set GPIO0_IODIR, 0xe0028008 + .set GPIO0_IOCLR, 0xe002800c + .set GPIO_IO_P11, 0x00000800 + +@ +@ No check for Thumb mode. The LPC2148 demo code doesn't do Thumb +@ +swiDispatch: + stmfd sp!, {r4, lr} @ Save r4, lr + ldr r4, [lr, #-4] @ Fetch the SWI instruction that invoked us + bic r4, r4, #0xff000000 @ Clear top 8 bits leaving swi "comment field"=number + cmp r4, #SWI_LAST @ <= SWI_LAST, handled in assembly, otherwise, in C handler + ldrls pc, [pc, r4, lsl #2] @ If <= SWI_LAST, we're handling it in assembly + b swiDispatchToC @ Not handled in here, switch to the C handler + +@ +@ Dispatch table for functions in assembly. The swiEntriesStart label isn't +@ referenced directly by the code. Rather, it must directly follow the +@ swiDispatch code, because it is relatively referenced. +@ +swiEntriesStart: + .word swiPYP @ 0 - FreeRTOS Yield Process + .word LED2Set @ 1 - Set LED2 state, return previous state + .word LED2On @ 2 - Turn LED2 on, return previous state + .word LED2Off @ 3 - Turn LED2 off, return previous state + .word LED2Toggle @ 4 - Toggle LED2, return previous state +swiEntriesEnd: + .set SWI_LAST, ((swiEntriesEnd-swiEntriesStart)/4)-1 + +@ +@ FreeRTOS uses SWI to yield process, but it's greedy and wants SWI all to its +@ self. So... Special handling it is! +@ +swiPYP: + ldmfd sp!, {r4, lr} @ Recover r4, lr + b vPortYieldProcessor @ Yield... yield like the wind! + +@ +@ Values are returned in r0. LED2 is hardwired in this code (bad programmer!) Also +@ remember that LED2 (and LED1) have their anodes tied to +3.3V, so we have to drive +@ the port pin LOW to turn them on. When the caller wants to turn an LED off, it +@ passes in a non-zero value (keeping the same software logic as the hardware logic) +@ +LED2Set: + stmfd sp!, {r1} @ Save r1 + cmp r0, #0 @ Turn LED2 off? + ldreq r4, =GPIO0_IOCLR @ Get GPIO0_IOCLR address (0 == on) + ldrne r4, =GPIO0_IOSET @ Get GPIO0_IOSET address (!0 == off) + ldr r0, =GPIO0_IOPIN @ Load GPIO0_IOPIN address + ldr r0, [r0] @ Get GPIO0_IOPIN contents + ldr r1, =GPIO_IO_P11 @ Constant for port pin LED2 attached to + and r0, r0, r1 @ R0 has bit that reflects LED2 current state + str r1, [r4] @ Set LED2 on + ldmfd sp!, {r1} @ Recover r1 + b swiExit @ Exit SWI handler + +LED2On: + stmfd sp!, {r1} @ Save r1 + ldr r0, =GPIO0_IOPIN @ Load GPIO0_IOPIN address + ldr r0, [r0] @ Get GPIO0_IOPIN contents + ldr r1, =GPIO_IO_P11 @ Constant for port pin LED2 attached to + and r0, r0, r1 @ R0 has bit that reflects LED2 current state + ldr r4, =GPIO0_IOCLR @ Get GPIO0_IOCLR address + str r1, [r4] @ Set LED2 on + ldmfd sp!, {r1} @ Recover r1 + b swiExit @ Exit SWI handler + +LED2Off: + stmfd sp!, {r1} @ Save r1 + ldr r0, =GPIO0_IOPIN @ Load GPIO0_IOPIN address + ldr r0, [r0] @ Get GPIO0_IOPIN contents + ldr r1, =GPIO_IO_P11 @ Constant for port pin LED2 attached to + and r0, r0, r1 @ R0 has bit that reflects LED2 current state + ldr r4, =GPIO0_IOSET @ Get GPIO0_IOSET address + str r1, [r4] @ Set LED2 off + ldmfd sp!, {r1} @ Recover r1 + b swiExit @ Exit SWI handler + +LED2Toggle: + stmfd sp!, {r1} @ Save r1 + ldr r0, =GPIO0_IOPIN @ Load GPIO0_IOPIN address + ldr r0, [r0] @ Get GPIO0_IOPIN contents + ldr r1, =GPIO_IO_P11 @ Constant for port pin LED2 attached to + ands r0, r0, r1 @ R0 has bit that reflects LED2 current state + ldrne r4, =GPIO0_IOCLR @ Get GPIO0_IOCLR address (!0 == off, so turn on) + ldreq r4, =GPIO0_IOSET @ Get GPIO0_IOSET address (0 == on, so turn off) + str r1, [r4] @ Set LED2 off + ldmfd sp!, {r1} @ Recover r1 + b swiExit @ Exit SWI handler + +@ +@ Not an internal function, try the C dispatch handler. r0, r1, r2 will be +@ passed through as set. r3 will contain the SWI number. Return value will +@ be in r0. +@ +swiDispatchToC: + stmfd sp!,{r1-r12, lr} @ Save r1..r12, lr on stack + mrs r12, spsr @ Saved Process Status Register (SPSR) to r12 + stmfd sp!, {r12} @ Save r12 + ldr r12, =swiDispatchC @ Get the address of the C handler function + mov r3, r4 @ Pass SWI number as parameter in r3 + mov lr, pc @ Save current PC to lr + bx r12 @ Branch to r12 (C handler function), return to next instruction + ldmfd sp!, {r12} @ Recover old SPSR to r12 + msr spsr_cxsf, r12 @ Set SPSR from r12 + ldmfd sp!, {r1-r12, pc}^ @ Recover r1..r12, return value in r0 + b swiExit @ And exit + +@ +@ Exit SWI handler. Recover r4, return to caller +@ +swiExit: + ldmfd sp!, {r4, pc}^ @ Recover r4, return to caller + + .endfunc + .end diff --git a/sysdefs.h b/sysdefs.h new file mode 100644 index 0000000..4d0e749 --- /dev/null +++ b/sysdefs.h @@ -0,0 +1,52 @@ +#ifndef _SYSDEFS_H_ +#define _SYSDEFS_H_ + +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef FALSE +#define FALSE (0) +#endif + +typedef unsigned char U8; +typedef signed char N8; +typedef unsigned short U16; +typedef signed short N16; +typedef unsigned int U32; +typedef signed int N32; +typedef int BOOL; + +typedef volatile U8 REG8; +typedef volatile U16 REG16; +typedef volatile U32 REG32; + +#define pREG8 (REG8 *) +#define pREG16 (REG16 *) +#define pREG32 (REG32 *) + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#define MIN(x,y) ((x)<(y)?(x):(y)) +#define MAX(x,y)((x)>(y)?(x):(y)) +#define arrsizeof(x) ((sizeof (x))/(sizeof (x [0]))) + +// +// Yuck. Don't like this here, but what the heck... +// +#if !defined CFG_CONSOLE_USB && !defined CFG_CONSOLE_UART0 && !defined CFG_CONSOLE_UART1 +#error "Must define CFG_CONSOLE_USB, CFG_CONSOLE_UART0 or CFG_CONSOLE_UART1 +#endif + +#if defined CFG_CONSOLE_USB && (defined CFG_CONSOLE_UART0 || defined CFG_CONSOLE_UART1) +#error "Only one of CFG_CONSOLE_USB, CFG_CONSOLE_UART0 or CFG_CONSOLE_UART1 may be defined" +#endif +#if defined CFG_CONSOLE_UART0 && (defined CFG_CONSOLE_USB || defined CFG_CONSOLE_UART1) +#error "Only one of CFG_CONSOLE_USB, CFG_CONSOLE_UART0 or CFG_CONSOLE_UART1 may be defined" +#endif +#if defined CFG_CONSOLE_UART1 && (defined CFG_CONSOLE_USB || defined CFG_CONSOLE_UART0) +#error "Only one of CFG_CONSOLE_USB, CFG_CONSOLE_UART0 or CFG_CONSOLE_UART1 may be defined" +#endif + +#endif diff --git a/uart/Makefile b/uart/Makefile new file mode 100644 index 0000000..a7bd147 --- /dev/null +++ b/uart/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=uart.c uartISR.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/uart/uart.c b/uart/uart.c new file mode 100644 index 0000000..acc2ef7 --- /dev/null +++ b/uart/uart.c @@ -0,0 +1,350 @@ +/* + INTERRUPT DRIVEN SERIAL PORT DRIVER FOR UART0 and UART1. + + This file contains all the serial port components that can be compiled to + either ARM or THUMB mode. Components that must be compiled to ARM mode are + contained in serialISR.c. + */ + +// +// Standard includes +// +#include + +// +// Scheduler includes +// +#include "FreeRTOS.h" +#include "queue.h" +#include "task.h" + +// +// Demo application includes +// +#include "uart.h" +#include "uartISR.h" + +// +// Constants to setup and access the UART +// +#define serWANTED_CLOCK_SCALING ((unsigned portLONG) 16) + +// +// Constants to setup and access the VIC +// +#define serINVALID_QUEUE ((xQueueHandle) 0) +#define serHANDLE ((xComPortHandle) 1) +#define serNO_BLOCK ((portTickType) 0) + +// +// Queues used to hold received characters, and characters waiting to be transmitted +// +static xQueueHandle xRX0Queue; +static xQueueHandle xTX0Queue; +static xQueueHandle xRX1Queue; +static xQueueHandle xTX1Queue; + +// +// Communication flag between the interrupt service routine and serial API +// +static volatile portLONG *plTHREEmpty; +static volatile portLONG *plTHREEmpty1; + +// +// +// +xComPortHandle uartInit (portCHAR pxPort, unsigned portLONG ulWantedBaud, unsigned portBASE_TYPE uxQueueLength) +{ + unsigned portLONG ulDivisor; + unsigned portLONG ulWantedClock; + xComPortHandle xReturn = serHANDLE; + + switch (pxPort) + { + case 0 : + { + uartISRCreateQueues (0, uxQueueLength, &xRX0Queue, &xTX0Queue, &plTHREEmpty); + + if ((xRX0Queue != serINVALID_QUEUE) && + (xTX0Queue != serINVALID_QUEUE) && + (ulWantedBaud != (unsigned portLONG) 0) + ) + { + portENTER_CRITICAL (); + + { + SCB_PCONP |= SCB_PCONP_PCUART0; + + // + // Setup the baud rate: Calculate the divisor value + // + ulWantedClock = ulWantedBaud * serWANTED_CLOCK_SCALING; + ulDivisor = configCPU_CLOCK_HZ / ulWantedClock; + + // + // Set the DLAB bit so we can access the divisor + // + UART0_LCR |= UART_LCR_DLAB; + + // + // Setup the divisor + // + UART0_DLL = (unsigned portCHAR) (ulDivisor & (unsigned portLONG) 0xff); + ulDivisor >>= 8; + UART0_DLM = (unsigned portCHAR) (ulDivisor & (unsigned portLONG) 0xff); + + // + // Turn on the FIFO's and clear the buffers + // + UART0_FCR = UART_FCR_EN | UART_FCR_CLR; + + // + // Setup transmission format + // + UART0_LCR = UART_LCR_NOPAR | UART_LCR_1STOP | UART_LCR_8BITS; + + // + // Setup the VIC for the UART + // + VIC_IntSelect &= ~VIC_IntSelect_UART0; + VIC_VectAddr2 = (portLONG) uartISR0; + VIC_VectCntl2 = VIC_VectCntl_ENABLE | VIC_Channel_UART0; + VIC_IntEnable = VIC_IntEnable_UART0; + + // + // Enable UART0 interrupts + // + UART0_IER |= UART_IER_EI; + } + + portEXIT_CRITICAL (); + } + else + xReturn = (xComPortHandle) 0; + } + break; + + case 1: + { + uartISRCreateQueues (1, uxQueueLength, &xRX1Queue, &xTX1Queue, &plTHREEmpty1); + + if ((xRX1Queue != serINVALID_QUEUE) && + (xTX1Queue != serINVALID_QUEUE) && + (ulWantedBaud != (unsigned portLONG) 0) + ) + { + portENTER_CRITICAL (); + + { + SCB_PCONP |= SCB_PCONP_PCUART1; + + // + // Setup the baud rate: Calculate the divisor value + // + ulWantedClock = ulWantedBaud * serWANTED_CLOCK_SCALING; + ulDivisor = configCPU_CLOCK_HZ / ulWantedClock; + + // + // Set the DLAB bit so we can access the divisor + // + UART1_LCR |= UART_LCR_DLAB; + + // + // Setup the divisor + // + UART1_DLL = (unsigned portCHAR) (ulDivisor & (unsigned portLONG) 0xff); + ulDivisor >>= 8; + UART1_DLM = (unsigned portCHAR) (ulDivisor & (unsigned portLONG) 0xff); + + // + // Turn on the FIFO's and clear the buffers + // + UART1_FCR = UART_FCR_EN | UART_FCR_CLR; + + // + // Setup transmission format + // + UART1_LCR = UART_LCR_NOPAR | UART_LCR_1STOP | UART_LCR_8BITS; + + // + // Setup the VIC for the UART + // + VIC_IntSelect &= ~VIC_IntSelect_UART1; + VIC_VectAddr3 = (portLONG) uartISR1; + VIC_VectCntl3 = VIC_VectCntl_ENABLE | VIC_Channel_UART1; + VIC_IntEnable = VIC_IntEnable_UART1; + + // + // Enable UART0 interrupts// + // + UART1_IER |= UART_IER_EI; + } + + portEXIT_CRITICAL (); + } + else + xReturn = (xComPortHandle) 0; + } + break; + } + + return xReturn; +} + +signed portBASE_TYPE uartGetChar (portCHAR pxPort, signed portCHAR *pcRxedChar, portTickType xBlockTime) +{ + switch (pxPort) + { + // + // Get the next character from the buffer. Return false if no characters are available, or arrive before xBlockTime expires + // + case 0: + { + if (xQueueReceive (xRX0Queue, pcRxedChar, xBlockTime)) + return pdTRUE; + else + return pdFALSE; + } + break; + + // + // Get the next character from the buffer. Return false if no characters are available, or arrive before xBlockTime expires + // + case 1: + { + if (xQueueReceive (xRX1Queue, pcRxedChar, xBlockTime)) + return pdTRUE; + else + return pdFALSE; + } + break; + } + + return pdFALSE; +} + +// +// +// +void uartPutString (portCHAR pxPort, const signed portCHAR * const pcString, portTickType xBlockTime) +{ + signed portCHAR *pxNext; + + pxNext = (signed portCHAR *) pcString; + + while (*pxNext) + { + uartPutChar (pxPort, *pxNext, xBlockTime); + pxNext++; + } +} + +// +// +// +signed portBASE_TYPE uartPutChar (portCHAR pxPort, signed portCHAR cOutChar, portTickType xBlockTime) +{ + signed portBASE_TYPE xReturn = 0; + + switch (pxPort) + { + case 0 : + { + portENTER_CRITICAL (); + + { + // + // Is there space to write directly to the UART? + // + if (*plTHREEmpty == (portLONG) pdTRUE) + { + *plTHREEmpty = pdFALSE; + UART0_THR = cOutChar; + xReturn = pdPASS; + } + else + { + // + // We cannot write directly to the UART, so queue the character. Block for a maximum of + // xBlockTime if there is no space in the queue. + // + xReturn = xQueueSend (xTX0Queue, &cOutChar, xBlockTime); + + // + // Depending on queue sizing and task prioritisation: While we were blocked waiting to post + // interrupts were not disabled. It is possible that the serial ISR has emptied the Tx queue, + // in which case we need to start the Tx off again. + // + if ((*plTHREEmpty == (portLONG) pdTRUE) && (xReturn == pdPASS)) + { + xQueueReceive (xTX0Queue, &cOutChar, serNO_BLOCK); + *plTHREEmpty = pdFALSE; + UART0_THR = cOutChar; + } + } + } + + portEXIT_CRITICAL (); + } + break; + + case 1 : + { + portENTER_CRITICAL (); + + { + // + // Is there space to write directly to the UART? + // + if (*plTHREEmpty1 == (portLONG) pdTRUE) + { + *plTHREEmpty1 = pdFALSE; + UART1_THR = cOutChar; + xReturn = pdPASS; + } + else + { + // + // We cannot write directly to the UART, so queue the character. Block for a maximum of + // xBlockTime if there is no space in the queue. + // + xReturn = xQueueSend (xTX1Queue, &cOutChar, xBlockTime); + + // + // Depending on queue sizing and task prioritisation: While we were blocked waiting to post + // interrupts were not disabled. It is possible that the serial ISR has emptied the Tx queue, + // in which case we need to start the Tx off again. + // + if ((*plTHREEmpty1 == (portLONG) pdTRUE) && (xReturn == pdPASS)) + { + xQueueReceive (xTX1Queue, &cOutChar, serNO_BLOCK); + *plTHREEmpty1 = pdFALSE; + UART1_THR = cOutChar; + } + } + } + + portEXIT_CRITICAL (); + } + break; + + default: + return xReturn; + break; + } + + return xReturn; +} + +// +// +// +void uart0GetRxQueue (xQueueHandle *qh) +{ + *qh = xRX0Queue; +} + +void uart1GetRxQueue (xQueueHandle *qh) +{ + *qh = xRX1Queue; +} diff --git a/uart/uart.h b/uart/uart.h new file mode 100644 index 0000000..07f10b3 --- /dev/null +++ b/uart/uart.h @@ -0,0 +1,17 @@ +#ifndef _UART_H_ +#define _UART_H_ + +#include "FreeRTOS.h" +#include "queue.h" + +typedef void *xComPortHandle; + +xComPortHandle uartInit (portCHAR pxPort, unsigned portLONG ulWantedBaud, unsigned portBASE_TYPE uxQueueLength); +void uartPutString (portCHAR pxPort, const signed portCHAR * const pcString, portTickType xBlockTime); +signed portBASE_TYPE uartGetChar (portCHAR pxPort, signed portCHAR *pcRxedChar, portTickType xBlockTime); +signed portBASE_TYPE uartPutChar (portCHAR pxPort, signed portCHAR cOutChar, portTickType xBlockTime); +void uartClose (portCHAR xPort); +void uart0GetRxQueue (xQueueHandle *qh); +void uart1GetRxQueue (xQueueHandle *qh); + +#endif diff --git a/uart/uartISR.c b/uart/uartISR.c new file mode 100644 index 0000000..a37b6c4 --- /dev/null +++ b/uart/uartISR.c @@ -0,0 +1,193 @@ +// +// Standard includes +// +#include + +// +// Scheduler includes +// +#include "FreeRTOS.h" +#include "queue.h" +#include "task.h" + +// +// Demo application includes +// +#include "uart.h" +#include "uartISR.h" + +// +// Constants to determine the ISR source +// +#define serSOURCE_THRE ((unsigned portCHAR) 0x02) +#define serSOURCE_RX_TIMEOUT ((unsigned portCHAR) 0x0c) +#define serSOURCE_ERROR ((unsigned portCHAR) 0x06) +#define serSOURCE_RX ((unsigned portCHAR) 0x04) +#define serINTERRUPT_SOURCE_MASK ((unsigned portCHAR) 0x0f) + +// +// Queues used to hold received characters, and characters waiting to be transmitted +// +static xQueueHandle xRX0Queue; +static xQueueHandle xTX0Queue; +static volatile portLONG lTHREEmpty0; +static xQueueHandle xRX1Queue; +static xQueueHandle xTX1Queue; +static volatile portLONG lTHREEmpty1; + +// +// +// +void uartISRCreateQueues (portCHAR pxPort, unsigned portBASE_TYPE uxQueueLength, xQueueHandle *pxRX0Queue, xQueueHandle *pxTX0Queue, portLONG volatile **pplTHREEmptyFlag) +{ + switch (pxPort) + { + case 0: + { + // + // Create the queues used to hold Rx and Tx characters + // + *pxRX0Queue = xRX0Queue = xQueueCreate (uxQueueLength, (unsigned portBASE_TYPE) sizeof (signed portCHAR)); + *pxTX0Queue = xTX0Queue = xQueueCreate (uxQueueLength + 1, (unsigned portBASE_TYPE) sizeof (signed portCHAR)); + + // + // Initialise the THRE empty flag - and pass back a reference + // + lTHREEmpty0 = (portLONG) pdTRUE; + *pplTHREEmptyFlag = &lTHREEmpty0; + } + break; + + case 1: + { + // + // Create the queues used to hold Rx and Tx characters + // + *pxRX0Queue = xRX1Queue = xQueueCreate (uxQueueLength, (unsigned portCHAR) sizeof (signed portCHAR)); + *pxTX0Queue = xTX1Queue = xQueueCreate (uxQueueLength + 1, (unsigned portCHAR) sizeof (signed portCHAR)); + + // + // Initialise the THRE empty flag - and pass back a reference + // + lTHREEmpty1 = (portLONG) pdTRUE; + *pplTHREEmptyFlag = &lTHREEmpty1; + } + break; + } +} + +// +// +// +void uartISR0 (void) __attribute__ ((naked)); +void uartISR0 (void) +{ + portENTER_SWITCHING_ISR (); + + signed portCHAR cChar; + portBASE_TYPE xTaskWokenByTx = pdFALSE; + portBASE_TYPE xTaskWokenByRx = pdFALSE; + + switch (UART0_IIR & serINTERRUPT_SOURCE_MASK) + { + // + // Not handling this, but clear the interrupt + // + case serSOURCE_ERROR : + { + cChar = UART0_LSR; + } + break; + + // + // The THRE is empty. If there is another character in the Tx queue, send it now, + // otherwise, no more characters, so indicate THRE is available + // + case serSOURCE_THRE : + { + if (xQueueReceiveFromISR (xTX0Queue, &cChar, &xTaskWokenByTx) == pdTRUE) + UART0_THR = cChar; + else + lTHREEmpty0 = pdTRUE; + } + break; + + // + // A character was received. Place it in the queue of received characters + // + case serSOURCE_RX_TIMEOUT : + case serSOURCE_RX : + { + cChar = UART0_RBR; + + if (xQueueSendFromISR (xRX0Queue, &cChar, (portBASE_TYPE) pdFALSE)) + xTaskWokenByRx = pdTRUE; + } + break; + + default : + break; + } + + VIC_VectAddr = (unsigned portLONG) 0; + + portEXIT_SWITCHING_ISR ((xTaskWokenByTx || xTaskWokenByRx)); +} + +// +// +// +void uartISR1 (void) __attribute__ ((naked)); +void uartISR1 (void) +{ + portENTER_SWITCHING_ISR (); + + signed portCHAR cChar; + portBASE_TYPE xTaskWokenByTx = pdFALSE; + portBASE_TYPE xTaskWokenByRx = pdFALSE; + + switch (UART1_IIR & serINTERRUPT_SOURCE_MASK) + { + // + // Not handling this, but clear the interrupt + // + case serSOURCE_ERROR : + { + cChar = UART1_LSR; + } + break; + + // + // The THRE is empty. If there is another character in the Tx queue, send it now, + // otherwise, no more characters, so indicate THRE is available + // + case serSOURCE_THRE : + { + if (xQueueReceiveFromISR (xTX1Queue, &cChar, &xTaskWokenByTx) == pdTRUE) + UART1_THR = cChar; + else + lTHREEmpty1 = pdTRUE; + } + break; + + // + // A character was received. Place it in the queue of received characters + // + case serSOURCE_RX_TIMEOUT : + case serSOURCE_RX : + { + cChar = UART1_RBR; + + if (xQueueSendFromISR (xRX1Queue, &cChar, (portBASE_TYPE) pdFALSE)) + xTaskWokenByRx = pdTRUE; + } + break; + + default : + break; + } + + VIC_VectAddr = (unsigned portLONG) 0; + + portEXIT_SWITCHING_ISR ((xTaskWokenByTx || xTaskWokenByRx)); +} diff --git a/uart/uartISR.h b/uart/uartISR.h new file mode 100644 index 0000000..8926373 --- /dev/null +++ b/uart/uartISR.h @@ -0,0 +1,16 @@ +#ifndef _UARTISR_H_ +#define _UARTISR_H_ + +#include "FreeRTOS.h" +#include "queue.h" + +// +// +// +void uartISRCreateQueues (portCHAR pxPort, unsigned portBASE_TYPE uxQueueLength, xQueueHandle *pxRX0Queue, xQueueHandle *pxTX0Queue, portLONG volatile **pplTHREEmptyFlag); +void uartISR0 (void); +void uartISR1 (void); +signed portBASE_TYPE uart0PostSpecialFromISR (U8 special); +signed portBASE_TYPE uart1PostSpecialFromISR (U8 special); + +#endif diff --git a/usb/Makefile b/usb/Makefile new file mode 100644 index 0000000..c6eaef1 --- /dev/null +++ b/usb/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=usbISR.c usbcontrol.c usbinit.c usbstdreq.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/usb/usbISR.c b/usb/usbISR.c new file mode 100644 index 0000000..1f28b03 --- /dev/null +++ b/usb/usbISR.c @@ -0,0 +1,411 @@ +#include "FreeRTOS.h" +#include "task.h" +// #include "usb.h" +#include "usbISR.h" +#include "usbapi.h" + +// +// +// +static TFnDevIntHandler *_pfnDevIntHandler = NULL; /** Installed device interrupt handler */ +static TFnEPIntHandler *_apfnEPIntHandlers [16]; /** Installed endpoint interrupt handlers */ +static TFnFrameHandler *_pfnFrameHandler = NULL; /** Installed frame interrupt handlers */ + +#define EP2IDX(bEP) ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7)) /** convert from endpoint address to endpoint index */ +#define IDX2EP(idx) ((((idx)<<7)&0x80)|(((idx)>>1)&0xF)) /** convert from endpoint index to endpoint address */ + +static void usbISR (void) __attribute__ ((naked)); + +// +// Local function to wait for a device interrupt (and clear it) +// +static void usbWaitForDeviceInterrupt (U32 dwIntr) +{ + while ((USB_DevIntSt & dwIntr) != dwIntr) + ; + + USB_DevIntClr = dwIntr; +} + +// +// Local function to send a command to the USB protocol engine +// +static void usbHardwareCommand (U8 bCmd) +{ + USB_DevIntClr = USB_DevIntClr_CDFULL | USB_DevIntClr_CCEMTY; + USB_CmdCode = 0x00000500 | (bCmd << 16); + usbWaitForDeviceInterrupt (USB_DevIntSt_CCEMTY); +} + +// +// Local function to send a command + data to the USB protocol engine +// +static void usbHardwareCommandWrite (U8 bCmd, U16 bData) +{ + usbHardwareCommand (bCmd); + USB_CmdCode = 0x00000100 | (bData << 16); + usbWaitForDeviceInterrupt (USB_DevIntSt_CCEMTY); +} + +// +// Local function to send a command to the USB protocol engine and read data +// +static U8 usbHardwareCommandRead (U8 bCmd) +{ + usbHardwareCommand (bCmd); + USB_CmdCode = 0x00000200 | (bCmd << 16); + usbWaitForDeviceInterrupt (USB_DevIntSt_CDFULL); + return USB_CmdData; +} + +// +// 'Realizes' an endpoint, meaning that buffer space is reserved for +// it. An endpoint needs to be realised before it can be used. +// +// From experiments, it appears that a USB reset causes USBReEP to +// re-initialise to 3 (= just the control endpoints). +// However, a USB bus reset does not disturb the USBMaxPSize settings. +// +static void usbHardwareEndpointRealize (int idx, U16 wMaxPSize) +{ + USB_ReEP |= (1 << idx); + USB_EpInd = idx; + USB_MaxPSize = wMaxPSize; + usbWaitForDeviceInterrupt (USB_DevIntSt_EPRLZED); +} + +// +// Enables or disables an endpoint +// +static void usbHardwareEndpointEnable (int idx, BOOL fEnable) +{ + usbHardwareCommandWrite (CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA); +} + +// +// Configures an endpoint and enables it +// +void usbHardwareEndpointConfig (U8 bEP, U16 wMaxPacketSize) +{ + int idx; + + idx = EP2IDX (bEP); + usbHardwareEndpointRealize (idx, wMaxPacketSize); + usbHardwareEndpointEnable (idx, TRUE); +} + +// +// Registers an endpoint event callback +// +void usbHardwareRegisterEPIntHandler (U8 bEP, TFnEPIntHandler *pfnHandler) +{ + int idx; + + idx = EP2IDX (bEP); + + _apfnEPIntHandlers [idx / 2] = pfnHandler; + USB_EpIntEn |= (1 << idx); + USB_DevIntEn |= USB_DevIntEn_EPSLOW; +} + +// +// Registers an device status callback +// +void usbHardwareRegisterDevIntHandler (TFnDevIntHandler *pfnHandler) +{ + _pfnDevIntHandler = pfnHandler; + USB_DevIntEn |= USB_DevIntEn_DEVSTAT; +} + +// +// Registers the frame callback +// +void usbHardwareRegisterFrameHandler (TFnFrameHandler *pfnHandler) +{ + _pfnFrameHandler = pfnHandler; + USB_DevIntEn |= USB_DevIntEn_FRAME; +} + +// +// Sets the USB address. +// +void usbHardwareSetAddress (U8 bAddr) +{ + usbHardwareCommandWrite (CMD_DEV_SET_ADDRESS, DEV_EN | bAddr); +} + +// +// Connects or disconnects from the USB bus +// +void usbHardwareConnect (BOOL fConnect) +{ + usbHardwareCommandWrite (CMD_DEV_STATUS, fConnect ? CON : 0); +} + +// +// Enables interrupt on NAK condition +// +// For IN endpoints a NAK is generated when the host wants to read data +// from the device, but none is available in the endpoint buffer. +// For OUT endpoints a NAK is generated when the host wants to write data +// to the device, but the endpoint buffer is still full. +// +// The endpoint interrupt handlers can distinguish regular (ACK) interrupts +// from NAK interrupt by checking the bits in their bEPStatus argument. +// +void usbHardwareNakIntEnable (U8 bIntBits) +{ + usbHardwareCommandWrite (CMD_DEV_SET_MODE, bIntBits); +} + +// +// Gets the stalled property of an endpoint +// +BOOL usbHardwareEndpointIsStalled (U8 bEP) +{ + int idx = EP2IDX (bEP); + + return (usbHardwareCommandRead (CMD_EP_SELECT | idx) & EP_STATUS_STALLED); +} + +// +// Sets the stalled property of an endpoint +// +void usbHardwareEndpointStall (U8 bEP, BOOL fStall) +{ + int idx = EP2IDX (bEP); + + usbHardwareCommandWrite (CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0); +} + +// +// Writes data to an endpoint buffer +// +int usbHardwareEndpointWrite (U8 bEP, U8 *pbBuf, int iLen) +{ + int idx; + + idx = EP2IDX (bEP); + + USB_Ctrl = USB_Ctrl_WREN | ((bEP & 0xF) << 2); + USB_TxPLen = iLen; + + while (USB_Ctrl & USB_Ctrl_WREN) + { + USB_TxData = (pbBuf [3] << 24) | (pbBuf [2] << 16) | (pbBuf [1] << 8) | pbBuf [0]; + pbBuf += 4; + } + + usbHardwareCommand (CMD_EP_SELECT | idx); + usbHardwareCommand (CMD_EP_VALIDATE_BUFFER); + + return iLen; +} + +// +// Reads data from an endpoint buffer +// +int usbHardwareEndpointRead (U8 bEP, U8 *pbBuf, int iMaxLen) +{ + int i, idx; + U32 dwData, dwLen; + + idx = EP2IDX (bEP); + + USB_Ctrl = USB_Ctrl_RDEN | ((bEP & 0xF) << 2); + + do + { + dwLen = USB_RxPLen; + } + while ((dwLen & USB_RxPLen_PKTRDY) == 0); + + if ((dwLen & USB_RxPLen_DV) == 0) + return -1; + + dwLen &= USB_RxPLen_PKTLENGTH_MASK; + + while (USB_Ctrl & USB_Ctrl_RDEN) + { + dwData = USB_RxData; + + if (pbBuf != NULL) + { + for (i = 0; i < 4; i++) + { + if (iMaxLen-- != 0) + *pbBuf++ = dwData & 0xFF; + + dwData >>= 8; + } + } + } + + usbHardwareCommand (CMD_EP_SELECT | idx); + usbHardwareCommand (CMD_EP_CLEAR_BUFFER); + + return dwLen; +} + + +// +// Sets the 'configured' state. +// +void usbHardwareConfigDevice (BOOL fConfigured) +{ + usbHardwareCommandWrite (CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0); +} + +// +// +// +void usbSetupInterruptHandler (void) +{ + // + // Set up USB interrupt, use highest priority ISR slot + // + VIC_IntSelect &= ~VIC_IntSelect_USB; + VIC_VectAddr1 = (portLONG) usbISR; + VIC_VectCntl1 = VIC_VectCntl_ENABLE | VIC_Channel_USB; + VIC_IntEnable = VIC_IntEnable_USB; +} + +// +// USB interrupt handler +// +void usbISR (void) +{ + portENTER_SWITCHING_ISR (); + + U32 dwStatus; + U32 dwIntBit; + U8 bEPStat, bDevStat, bStat; + int i; + portBASE_TYPE xTaskWoken = pdFALSE; + + dwStatus = USB_DevIntSt; + + if (dwStatus & USB_DevIntSt_DEVSTAT) + { + USB_DevIntClr = USB_DevIntClr_DEVSTAT; + bDevStat = usbHardwareCommandRead (CMD_DEV_STATUS); + + if (bDevStat & (CON_CH | SUS_CH | RST)) + { + bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) | + ((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) | + ((bDevStat & RST) ? DEV_STATUS_RESET : 0); + + if (_pfnDevIntHandler != NULL) + _pfnDevIntHandler (bStat); + } + } + + if (dwStatus & USB_DevIntSt_EPSLOW) + { + USB_DevIntClr = USB_DevIntClr_EPSLOW; + + for (i = 0; i < 32; i++) + { + dwIntBit = (1 << i); + + if (USB_EpIntSt & dwIntBit) + { + USB_EpIntClr = dwIntBit; + usbWaitForDeviceInterrupt (USB_DevIntSt_CDFULL); + bEPStat = USB_CmdData; + + bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) | + ((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) | + ((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) | + ((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) | + ((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0); + + if (bEPStat & (1 <<5)) + { + } + + if (bEPStat & (1 <<6)) + { + } + + if (_apfnEPIntHandlers [i / 2] != NULL) + xTaskWoken |= _apfnEPIntHandlers [i / 2] (IDX2EP (i), bStat); + } + } + } + + if (dwStatus & USB_DevIntSt_FRAME) + { + USB_DevIntClr = USB_DevIntClr_FRAME; + + if (_pfnFrameHandler != NULL) + _pfnFrameHandler (0); + } + + VIC_VectAddr = (unsigned portLONG) 0; + + portEXIT_SWITCHING_ISR (xTaskWoken); +} + +// +// Initializes the USB hardware +// +// This function assumes that the hardware is connected as shown in +// section 10.1 of the LPC2148 data sheet: +// * P0.31 controls a switch to connect a 1.5k pull-up to D+ if low. +// * P0.23 is connected to USB VCC. +// +BOOL usbHardwareInit (void) +{ + // + // Configure P0.23 for Vbus sense, P0.31 as USB connect indicator + // + PCB_PINSEL1 = (PCB_PINSEL1 & ~PCB_PINSEL1_P023_GPIO) | PCB_PINSEL1_P023_VBUS; + GPIO0_IODIR &= ~GPIO_IO_P23; + PCB_PINSEL1 = (PCB_PINSEL1 & ~PCB_PINSEL1_P031_GPIO) | PCB_PINSEL1_P031_CONNECT; + + // + // Enable PUSB + // + SCB_PCONP |= SCB_PCONP_PUSB; + + // + // Initialize PLL + // + USB_PLLCON = USB_PLLCON_PLLE; + USB_PLLCFG = USB_PLLCFG_DIV2 | USB_PLLCFG_MUL4; + USB_PLLFEED = USB_PLLFEED_FEED1; + USB_PLLFEED = USB_PLLFEED_FEED2; + + // + // Wait for PLL to lock up + // + while (!(USB_PLLSTAT & USB_PLLSTAT_PLOCK)) + ; + + USB_PLLCON = USB_PLLCON_PLLC | USB_PLLCON_PLLE; + USB_PLLFEED = USB_PLLFEED_FEED1; + USB_PLLFEED = USB_PLLFEED_FEED2; + + // + // Disable/clear all interrupts for now + // + USB_DevIntEn = USB_DevIntEn_NONE; + USB_EpIntEn = USB_EpIntEn_NONE; + USB_DevIntClr = USB_DevIntClr_ALL; + USB_EpIntClr = USB_EpIntClr_ALL; + + // + // Setup control endpoints + // + usbHardwareEndpointConfig (0x00, MAX_PACKET_SIZE0); + usbHardwareEndpointConfig (0x80, MAX_PACKET_SIZE0); + + // + // By default, only ACKs generate interrupts + // + usbHardwareNakIntEnable (0); + + return TRUE; +} diff --git a/usb/usbISR.h b/usb/usbISR.h new file mode 100644 index 0000000..2b1f46c --- /dev/null +++ b/usb/usbISR.h @@ -0,0 +1,98 @@ +// +// These really need to be normalized to the names in lp210x.h and included there. +// A #define like "DEV_ADDR" is just too ambiguous when it's unqualified with +// what sub-system it belongs to. +// + +#ifndef _USBISR_H_ +#define _USBISR_H_ + +// +// Protocol engine command codes, device commands +// +#define CMD_DEV_SET_ADDRESS 0xD0 +#define CMD_DEV_CONFIG 0xD8 +#define CMD_DEV_SET_MODE 0xF3 +#define CMD_DEV_READ_CUR_FRAME_NR 0xF5 +#define CMD_DEV_READ_TEST_REG 0xFD +#define CMD_DEV_STATUS 0xFE /* read/write */ +#define CMD_DEV_GET_ERROR_CODE 0xFF +#define CMD_DEV_READ_ERROR_STATUS 0xFB + +// +// Protocol engine command codes, endpoint commands +// +#define CMD_EP_SELECT 0x00 +#define CMD_EP_SELECT_CLEAR 0x40 +#define CMD_EP_SET_STATUS 0x40 +#define CMD_EP_CLEAR_BUFFER 0xF2 +#define CMD_EP_VALIDATE_BUFFER 0xFA + +// +// Set address command +// +#define DEV_ADDR (1<<0) +#define DEV_EN (1<<7) + +// +// Configure device command +// +#define CONF_DEVICE (1<<0) + +// +// Set mode command +// +#define AP_CLK (1<<0) +#define INAK_CI (1<<1) +#define INAK_CO (1<<2) +#define INAK_II (1<<3) +#define INAK_IO (1<<4) +#define INAK_BI (1<<5) +#define INAK_BO (1<<6) + +// +// Set get device status command +// +#define CON (1<<0) +#define CON_CH (1<<1) +#define SUS (1<<2) +#define SUS_CH (1<<3) +#define RST (1<<4) + +// +// Get error code command +// ... +// + +// +// Select Endpoint command read bits +// +#define EPSTAT_FE (1<<0) +#define EPSTAT_ST (1<<1) +#define EPSTAT_STP (1<<2) +#define EPSTAT_PO (1<<3) +#define EPSTAT_EPN (1<<4) +#define EPSTAT_B1FULL (1<<5) +#define EPSTAT_B2FULL (1<<6) + +// +// CMD_EP_SET_STATUS command +// +#define EP_ST (1<<0) +#define EP_DA (1<<5) +#define EP_RF_MO (1<<6) +#define EP_CND_ST (1<<7) + +// +// Read error status command +// +#define PID_ERR (1<<0) +#define UEPKT (1<<1) +#define DCRC (1<<2) +#define TIMEOUT (1<<3) +#define EOP (1<<4) +#define B_OVRN (1<<5) +#define BTSTF (1<<6) +#define TGL_ERR (1<<7) + +#endif diff --git a/usb/usbapi.h b/usb/usbapi.h new file mode 100644 index 0000000..9ee50f4 --- /dev/null +++ b/usb/usbapi.h @@ -0,0 +1,92 @@ +#include "usbstruct.h" // for TSetupPacket + +/************************************************************************* + USB configuration +**************************************************************************/ + +#define MAX_PACKET_SIZE0 64 /**< maximum packet size for EP 0 */ + +/************************************************************************* + USB hardware interface +**************************************************************************/ + +// endpoint status sent through callback +#define EP_STATUS_DATA (1<<0) /**< EP has data */ +#define EP_STATUS_STALLED (1<<1) /**< EP is stalled */ +#define EP_STATUS_SETUP (1<<2) /**< EP received setup packet */ +#define EP_STATUS_ERROR (1<<3) /**< EP data was overwritten by setup packet */ +#define EP_STATUS_NACKED (1<<4) /**< EP sent NAK */ + +// device status sent through callback +#define DEV_STATUS_CONNECT (1<<0) /**< device just got connected */ +#define DEV_STATUS_SUSPEND (1<<2) /**< device entered suspend state */ +#define DEV_STATUS_RESET (1<<4) /**< device just got reset */ + +// interrupt bits for NACK events in usbHardwareNakIntEnable +// (these bits conveniently coincide with the LPC214x USB controller bit) +#define INACK_CI (1<<1) /**< interrupt on NACK for control in */ +#define INACK_CO (1<<2) /**< interrupt on NACK for control out */ +#define INACK_II (1<<3) /**< interrupt on NACK for interrupt in */ +#define INACK_IO (1<<4) /**< interrupt on NACK for interrupt out */ +#define INACK_BI (1<<5) /**< interrupt on NACK for bulk in */ +#define INACK_BO (1<<6) /**< interrupt on NACK for bulk out */ + +BOOL usbHardwareInit (void); +void usbSetupInterruptHandler (void); +void usbHardwareNakIntEnable (U8 bIntBits); +void usbHardwareConnect (BOOL fConnect); +void usbHardwareSetAddress (U8 bAddr); +void usbHardwareConfigDevice (BOOL fConfigured); + +// +// Endpoint operations +// +void usbHardwareEndpointConfig (U8 bEP, U16 wMaxPacketSize); +int usbHardwareEndpointRead (U8 bEP, U8 *pbBuf, int iMaxLen); +int usbHardwareEndpointWrite (U8 bEP, U8 *pbBuf, int iLen); +void usbHardwareEndpointStall (U8 bEP, BOOL fStall); +BOOL usbHardwareEndpointIsStalled (U8 bEP); + +// +// Endpoint interrupt handler callback +// +typedef int (TFnEPIntHandler) (U8 bEP, U8 bEPStatus); +void usbHardwareRegisterEPIntHandler (U8 bEP, TFnEPIntHandler *pfnHandler); + +// +// Device status handler callback +// +typedef void (TFnDevIntHandler) (U8 bDevStatus); +void usbHardwareRegisterDevIntHandler (TFnDevIntHandler *pfnHandler); + +// +// Frame event handler callback +// +typedef void (TFnFrameHandler)(U16 wFrame); +void usbHardwareRegisterFrameHandler(TFnFrameHandler *pfnHandler); + + +/************************************************************************* + USB application interface +**************************************************************************/ + +// initialise the complete stack, including HW +BOOL usbRegisterHandlers (void); + +/** Request handler callback (standard, vendor, class) */ +typedef BOOL (TFnHandleRequest)(TSetupPacket *pSetup, int *piLen, U8 **ppbData); +void usbRegisterRequestHandler (int iType, TFnHandleRequest *pfnHandler, U8 *pbDataStore); +void usbRegisterCustomReqHandler (TFnHandleRequest *pfnHandler); + +/** Descriptor handler callback */ +typedef BOOL (TFnGetDescriptor)(U16 wTypeIndex, U16 wLangID, int *piLen, U8 **ppbData); + +/** Default standard request handler */ +BOOL usbHandleStandardRequest (TSetupPacket *pSetup, int *piLen, U8 **ppbData); + +/** Default EP0 handler */ +int usbHandleControlTransfer (U8 bEP, U8 bEPStat); + +/** Descriptor handling */ +void usbRegisterDescriptors (const U8 *pabDescriptors); +BOOL usbGetDescriptor (U16 wTypeIndex, U16 wLangID, int *piLen, U8 **ppbData); diff --git a/usb/usbcontrol.c b/usb/usbcontrol.c new file mode 100644 index 0000000..a29f7d3 --- /dev/null +++ b/usb/usbcontrol.c @@ -0,0 +1,173 @@ +/** @file + Control transfer handler. + + This module handles control transfers and is normally installed on the + endpoint 0 callback. + + Control transfers can be of the following type: + 0 Standard; + 1 Class; + 2 Vendor; + 3 Reserved. + + A callback can be installed for each of these control transfers using + USBRegisterRequestHandler. + When an OUT request arrives, data is collected in the data store provided + with the usbRegisterRequestHandler call. When the transfer is done, the + callback is called. + When an IN request arrives, the callback is called immediately to either + put the control transfer data in the data store, or to get a pointer to + control transfer data. The data is then packetised and sent to the host. +*/ + +#include "FreeRTOS.h" + +#include "usbstruct.h" +#include "usbapi.h" + +// +// +// +#define MAX_CONTROL_SIZE 128 /**< maximum total size of control transfer data */ +#define MAX_REQ_HANDLERS 4 /**< standard, class, vendor, reserved */ + +static TSetupPacket Setup; /**< setup packet */ + +static U8 *pbData; /**< pointer to data buffer */ +static int iResidue; /**< remaining bytes in buffer */ +static int iLen; /**< total length of control transfer */ + +static TFnHandleRequest *apfnReqHandlers [4] = { NULL, NULL, NULL, NULL }; // Array of installed request handler callbacks +static U8 *apbDataStore [4] = { NULL, NULL, NULL, NULL }; // Array of installed request data pointers */ + +// +// Local function to handle a request by calling one of the installed +// request handlers. + +// In case of data going from host to device, the data is at *ppbData. +// In case of data going from device to host, the handler can either +// choose to write its data at *ppbData or update the data pointer. +// +static BOOL _HandleRequest (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + TFnHandleRequest *pfnHandler; + int iType; + + iType = REQTYPE_GET_TYPE (pSetup->bmRequestType); + pfnHandler = apfnReqHandlers [iType]; + + if (pfnHandler == NULL) + return FALSE; + + return pfnHandler (pSetup, piLen, ppbData); +} + +// +// Local function to stall the control endpoint +// +static void usbStallControlPipe (U8 bEPStat __attribute__ ((unused))) +{ + usbHardwareEndpointStall (0x80, TRUE); +} + +// +// Sends next chunk of data (possibly 0 bytes) to host +// +static void usbDataIn (void) +{ + int iChunk; + + iChunk = MIN (MAX_PACKET_SIZE0, iResidue); + usbHardwareEndpointWrite (0x80, pbData, iChunk); + pbData += iChunk; + iResidue -= iChunk; +} + +// +// Handles IN/OUT transfers on EP0 +// +int usbHandleControlTransfer (U8 bEP, U8 bEPStat) +{ + int iChunk; + int iType; + + // + // OUT transfer + // + if (bEP == 0x00) + { + if (bEPStat & EP_STATUS_SETUP) + { + // + // Setup packet, reset request message state machine + // + usbHardwareEndpointRead (0x00, (U8 *) &Setup, sizeof (Setup)); + + // + // Defaults for data pointer and residue + // + iType = REQTYPE_GET_TYPE (Setup.bmRequestType); + pbData = apbDataStore [iType]; + iResidue = Setup.wLength; + iLen = Setup.wLength; + + if ((Setup.wLength == 0) || (REQTYPE_GET_DIR(Setup.bmRequestType) == REQTYPE_DIR_TO_HOST)) + { + if (!_HandleRequest (&Setup, &iLen, &pbData)) + { + usbStallControlPipe (bEPStat); + return pdFALSE; + } + + iResidue = MIN (iLen, Setup.wLength); + usbDataIn (); + } + } + else + { + if (iResidue > 0) + { + iChunk = usbHardwareEndpointRead (0x00, pbData, iResidue); + + if (iChunk < 0) + { + usbStallControlPipe (bEPStat); + return pdFALSE; + } + + pbData += iChunk; + iResidue -= iChunk; + + if (iResidue == 0) + { + iType = REQTYPE_GET_TYPE (Setup.bmRequestType); + pbData = apbDataStore [iType]; + + if (!_HandleRequest (&Setup, &iLen, &pbData)) + { + usbStallControlPipe (bEPStat); + return pdFALSE; + } + + usbDataIn (); + } + } + else + iChunk = usbHardwareEndpointRead (0x00, NULL, 0); + } + } + else if (bEP == 0x80) + usbDataIn (); + + return pdFALSE; +} + + +// +// Registers a callback for handling requests +// +void usbRegisterRequestHandler (int iType, TFnHandleRequest *pfnHandler, U8 *pbDataStore) +{ + apfnReqHandlers [iType] = pfnHandler; + apbDataStore [iType] = pbDataStore; +} diff --git a/usb/usbinit.c b/usb/usbinit.c new file mode 100644 index 0000000..02e2181 --- /dev/null +++ b/usb/usbinit.c @@ -0,0 +1,30 @@ +#include "FreeRTOS.h" +#include "usbapi.h" + + +// +// Data storage area for standard requests +// +static U8 abStdReqData [8]; + + +// +// USB reset handler +// +static void usbHandleReset(U8 bDevStatus __attribute__ ((unused))) +{ +} + +// +// +// +BOOL usbRegisterHandlers (void) +{ + usbHardwareInit (); + usbHardwareRegisterDevIntHandler (usbHandleReset); + usbHardwareRegisterEPIntHandler (0x00, usbHandleControlTransfer); + usbHardwareRegisterEPIntHandler (0x80, usbHandleControlTransfer); + usbRegisterRequestHandler (REQTYPE_TYPE_STANDARD, usbHandleStandardRequest, abStdReqData); + + return TRUE; +} diff --git a/usb/usbstdreq.c b/usb/usbstdreq.c new file mode 100644 index 0000000..96edc2f --- /dev/null +++ b/usb/usbstdreq.c @@ -0,0 +1,349 @@ +/** @file + Standard request handler. + + This modules handles the 'chapter 9' processing, specifically the + standard device requests in table 9-3 from the universal serial bus + specification revision 2.0 + + Specific types of devices may specify additional requests (for example + HID devices add a GET_DESCRIPTOR request for interfaces), but they + will not be part of this module. + + @todo some requests have to return a request error if device not configured: + @todo GET_INTERFACE, GET_STATUS, SET_INTERFACE, SYNCH_FRAME + @todo this applies to the following if endpoint != 0: + @todo SET_FEATURE, GET_FEATURE +*/ + +#include "usbstruct.h" +#include "usbapi.h" + +#define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */ + + +// +// General descriptor field offsets +// +#define DESC_bLength 0 /**< length offset */ +#define DESC_bDescriptorType 1 /**< descriptor type offset */ + +// +// Config descriptor field offsets +// +#define CONF_DESC_wTotalLength 2 /**< total length offset */ +#define CONF_DESC_bConfigurationValue 5 /**< configuration value offset */ +#define CONF_DESC_bmAttributes 7 /**< configuration characteristics */ + +// +// Interface descriptor field offsets +// +#define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */ + +// +// Endpoint descriptor field offsets +// +#define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */ +#define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */ + + +static U8 bConfiguration = 0; /** Currently selected configuration */ +static TFnHandleRequest *pfnHandleCustomReq = NULL; /** Installed custom request handler */ +static const U8 *pabDescrip = NULL; /** Pointer to registered descriptors */ + + +// +// Registers a pointer to a descriptor block containing all descriptors for the device. +// +void usbRegisterDescriptors (const U8 *pabDescriptors) +{ + pabDescrip = pabDescriptors; +} + +// +// Parses the list of installed USB descriptors and attempts to find the specified USB descriptor. +// +BOOL usbGetDescriptor (U16 wTypeIndex, U16 wLangID __attribute__ ((unused)), int *piLen, U8 **ppbData) +{ + U8 bType, bIndex; + U8 *pab; + int iCurIndex; + + bType = GET_DESC_TYPE (wTypeIndex); + bIndex = GET_DESC_INDEX (wTypeIndex); + + pab = (U8 *)pabDescrip; + iCurIndex = 0; + + while (pab [DESC_bLength] != 0) + { + if (pab [DESC_bDescriptorType] == bType) + { + if (iCurIndex == bIndex) + { + *ppbData = pab; + + if (bType == DESC_CONFIGURATION) + *piLen = (pab [CONF_DESC_wTotalLength]) | (pab [CONF_DESC_wTotalLength + 1] << 8); + else + *piLen = pab [DESC_bLength]; + + return TRUE; + } + + iCurIndex++; + } + + pab += pab [DESC_bLength]; + } + + return FALSE; +} + +// +// Configures the device according to the specified configuration index and +// alternate setting by parsing the installed USB descriptor list. +// A configuration index of 0 unconfigures the device. +// +static BOOL usbSetConfiguration (U8 bConfigIndex, U8 bAltSetting) +{ + U8 *pab; + U8 bCurConfig, bCurAltSetting; + U8 bEP; + U16 wMaxPktSize; + + if (bConfigIndex == 0) + usbHardwareConfigDevice(FALSE); + else + { + pab = (U8 *) pabDescrip; + bCurConfig = 0xFF; + bCurAltSetting = 0xFF; + + while (pab [DESC_bLength] != 0) + { + switch (pab [DESC_bDescriptorType]) + { + case DESC_CONFIGURATION : + { + bCurConfig = pab [CONF_DESC_bConfigurationValue]; + } + break; + + case DESC_INTERFACE : + { + bCurAltSetting = pab [INTF_DESC_bAlternateSetting]; + } + break; + + case DESC_ENDPOINT : + { + if ((bCurConfig == bConfigIndex) && (bCurAltSetting == bAltSetting)) + { + bEP = pab [ENDP_DESC_bEndpointAddress]; + wMaxPktSize = (pab [ENDP_DESC_wMaxPacketSize]) | (pab [ENDP_DESC_wMaxPacketSize + 1] << 8); + usbHardwareEndpointConfig (bEP, wMaxPktSize); + } + } + break; + + default : + break; + } + + pab += pab [DESC_bLength]; + } + + usbHardwareConfigDevice (TRUE); + } + + return TRUE; +} + +// +// Local function to handle a standard device request +// +static BOOL usbHandleStdDeviceReq (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + U8 *pbData = *ppbData; + + switch (pSetup->bRequest) + { + case REQ_GET_STATUS : + { + pbData [0] = 0; + pbData [1] = 0; + *piLen = 2; + } + break; + + case REQ_SET_ADDRESS : + { + usbHardwareSetAddress (pSetup->wValue); + } + break; + + case REQ_GET_DESCRIPTOR : + return usbGetDescriptor (pSetup->wValue, pSetup->wIndex, piLen, ppbData); + + case REQ_GET_CONFIGURATION : + { + pbData [0] = bConfiguration; + *piLen = 1; + } + break; + + case REQ_SET_CONFIGURATION : + { + if (!usbSetConfiguration(pSetup->wValue & 0xFF, 0)) + return FALSE; + + bConfiguration = pSetup->wValue & 0xFF; + } + break; + + case REQ_CLEAR_FEATURE : + case REQ_SET_FEATURE : + { + if (pSetup->wValue == FEA_REMOTE_WAKEUP) { + } + if (pSetup->wValue == FEA_TEST_MODE) { + } + } + return FALSE; + + case REQ_SET_DESCRIPTOR : + return FALSE; + + default : + return FALSE; + } + + return TRUE; +} + +// +// Local function to handle a standard interface request +// +static BOOL usbHandleStdInterfaceReq (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + U8 *pbData = *ppbData; + + switch (pSetup->bRequest) + { + case REQ_GET_STATUS : + { + pbData [0] = 0; + pbData [1] = 0; + *piLen = 2; + } + break; + + case REQ_CLEAR_FEATURE : + case REQ_SET_FEATURE : + return FALSE; + + case REQ_GET_INTERFACE : + { + pbData [0] = 0; + *piLen = 1; + } + break; + + case REQ_SET_INTERFACE : + { + if (pSetup->wValue != 0) + return FALSE; + + *piLen = 0; + } + break; + + default : + return FALSE; + } + + return TRUE; +} + +// +// Local function to handle a standard endpoint request +// +static BOOL usbHandleStdEndPointReq (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + U8 *pbData = *ppbData; + + switch (pSetup->bRequest) + { + case REQ_GET_STATUS : + { + pbData [0] = usbHardwareEndpointIsStalled (pSetup->wIndex) ? 1 : 0; + pbData [1] = 0; + *piLen = 2; + } + break; + + case REQ_CLEAR_FEATURE : + { + if (pSetup->wValue == FEA_ENDPOINT_HALT) + { + usbHardwareEndpointStall(pSetup->wIndex, FALSE); + break; + } + } + return FALSE; + + case REQ_SET_FEATURE : + { + if (pSetup->wValue == FEA_ENDPOINT_HALT) + { + usbHardwareEndpointStall(pSetup->wIndex, TRUE); + break; + } + } + return FALSE; + + case REQ_SYNCH_FRAME : + return FALSE; + + default : + return FALSE; + } + + return TRUE; +} + + +// +// Default handler for standard ('chapter 9') requests +// If a custom request handler was installed, this handler is called first. +// +BOOL usbHandleStandardRequest (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq (pSetup, piLen, ppbData)) + return TRUE; + + switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) + { + case REQTYPE_RECIP_DEVICE : return usbHandleStdDeviceReq (pSetup, piLen, ppbData); + case REQTYPE_RECIP_INTERFACE : return usbHandleStdInterfaceReq (pSetup, piLen, ppbData); + case REQTYPE_RECIP_ENDPOINT : return usbHandleStdEndPointReq (pSetup, piLen, ppbData); + default : return FALSE; + } +} + +// +// Registers a callback for custom device requests +// +// In usbHandleStandardRequest, the custom request handler gets a first +// chance at handling the request before it is handed over to the 'chapter 9' +// request handler. +// +// This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR +// request is sent to an interface, which is not covered by the 'chapter 9' +// specification. +// +void usbRegisterCustomReqHandler (TFnHandleRequest *pfnHandler) +{ + pfnHandleCustomReq = pfnHandler; +} + diff --git a/usb/usbstruct.h b/usb/usbstruct.h new file mode 100644 index 0000000..ee02a75 --- /dev/null +++ b/usb/usbstruct.h @@ -0,0 +1,127 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/** + Definitions of structures of standard USB packets +*/ + +#ifndef _USBSTRUCT_H_ +#define _USBSTRUCT_H_ + +#include "sysdefs.h" + +// +// Setup packet definitions +// +typedef struct +{ + U8 bmRequestType; /**< characteristics of the specific request */ + U8 bRequest; /**< specific request */ + U16 wValue; /**< request specific parameter */ + U16 wIndex; /**< request specific parameter */ + U16 wLength; /**< length of data transfered in data phase */ +} +TSetupPacket; + + +#define REQTYPE_GET_DIR(x) (((x)>>7)&0x01) +#define REQTYPE_GET_TYPE(x) (((x)>>5)&0x03) +#define REQTYPE_GET_RECIP(x) ((x)&0x1F) + +#define REQTYPE_DIR_TO_DEVICE 0 +#define REQTYPE_DIR_TO_HOST 1 + +#define REQTYPE_TYPE_STANDARD 0 +#define REQTYPE_TYPE_CLASS 1 +#define REQTYPE_TYPE_VENDOR 2 +#define REQTYPE_TYPE_RESERVED 3 + +#define REQTYPE_RECIP_DEVICE 0 +#define REQTYPE_RECIP_INTERFACE 1 +#define REQTYPE_RECIP_ENDPOINT 2 +#define REQTYPE_RECIP_OTHER 3 + +// +// Standard requests +// +#define REQ_GET_STATUS 0x00 +#define REQ_CLEAR_FEATURE 0x01 +#define REQ_SET_FEATURE 0x03 +#define REQ_SET_ADDRESS 0x05 +#define REQ_GET_DESCRIPTOR 0x06 +#define REQ_SET_DESCRIPTOR 0x07 +#define REQ_GET_CONFIGURATION 0x08 +#define REQ_SET_CONFIGURATION 0x09 +#define REQ_GET_INTERFACE 0x0A +#define REQ_SET_INTERFACE 0x0B +#define REQ_SYNCH_FRAME 0x0C + +// +// Class requests HID +// +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +// +// Feature selectors +// +#define FEA_ENDPOINT_HALT 0x00 +#define FEA_REMOTE_WAKEUP 0x01 +#define FEA_TEST_MODE 0x02 + +// +// USB descriptor header +// +typedef struct +{ + U8 bLength; /**< descriptor length */ + U8 bDescriptorType; /**< descriptor type */ +} +TUSBDescHeader; + +#define DESC_DEVICE 1 +#define DESC_CONFIGURATION 2 +#define DESC_STRING 3 +#define DESC_INTERFACE 4 +#define DESC_ENDPOINT 5 +#define DESC_DEVICE_QUALIFIER 6 +#define DESC_OTHER_SPEED 7 +#define DESC_INTERFACE_POWER 8 + +#define DESC_HID_HID 0x21 +#define DESC_HID_REPORT 0x22 +#define DESC_HID_PHYSICAL 0x23 + +#define GET_DESC_TYPE(x) (((x)>>8)&0xFF) +#define GET_DESC_INDEX(x) ((x)&0xFF) + +#endif diff --git a/usbmass/Makefile b/usbmass/Makefile new file mode 100644 index 0000000..0692453 --- /dev/null +++ b/usbmass/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=mscblock.c mscbot.c mscscsi.c usbmass.c mscspi.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/usbmass/mscblock.c b/usbmass/mscblock.c new file mode 100644 index 0000000..0f6d5a1 --- /dev/null +++ b/usbmass/mscblock.c @@ -0,0 +1,367 @@ +/*****************************************************************************\ +* efs - General purpose Embedded Filesystem library * +* --------------------- ----------------------------------- * +* * +* Filename : sd.c * +* Revision : Initial developement * +* Description : This file contains the functions needed to use efs for * +* accessing files on an SD-card. * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation; either * +* version 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* (c)2005 Michael De Nil * +* (c)2005 Lennart Yseboodt * +\*****************************************************************************/ + +/* + 2006, Bertrik Sikken, modified for LPCUSB +*/ + +#include "FreeRTOS.h" + +#include + +#include "../fatfs/diskio.h" +#include "../fatfs/ff.h" +#include "../fatfs/mmc.h" +#include "../fatfs/spi.h" +#include "mscdebug.h" +#include "mscblock.h" +#include "mscspi.h" + +// +// +// +#define CMD_GOIDLESTATE 0 +#define CMD_SENDOPCOND 1 +#define CMD_READCSD 9 +#define CMD_READCID 10 +#define CMD_SENDSTATUS 13 +#define CMD_READSINGLEBLOCK 17 +#define CMD_WRITE 24 +#define CMD_WRITE_MULTIPLE 25 + +// +// +// +static void mscblockCommand (U8 cmd, U32 param) +{ + U8 abCmd [8]; + + abCmd [0] = 0xff; + abCmd [1] = 0x40 | cmd; + abCmd [2] = (U8)(param >> 24); + abCmd [3] = (U8)(param >> 16); + abCmd [4] = (U8)(param >> 8); + abCmd [5] = (U8)(param); + abCmd [6] = 0x95; /* Checksum (should be only valid for first command (0) */ + abCmd [7] = 0xff; /* eat empty command - response */ + + mscspiSendBlock (abCmd, 8); +} + +// +// +// +static U8 mscblockResp8b (void) +{ + U8 i; + U8 resp; + + /* Respone will come after 1 - 8 pings */ + for (i = 0; i < 8; i++) + if ((resp = mscspiTransferByte (0xff)) != 0xff) + return resp; + + return resp; +} + +// +// +// +static void mscblockResp8bError (U8 value) +{ + switch (value) + { + case 0x40 : DBG ("Argument out of bounds.\n"); break; + case 0x20 : DBG ("Address out of bounds.\n"); break; + case 0x10 : DBG ("Error during erase sequence.\n"); break; + case 0x08 : DBG ("CRC failed.\n"); break; + case 0x04 : DBG ("Illegal command.\n"); break; + case 0x02 : DBG ("Erase reset (see SanDisk docs p5-13).\n"); break; + case 0x01 : DBG ("Card is initialising.\n"); break; + default : DBG ("Unknown error 0x%x (see SanDisk docs p5-13).\n", value); break; + } +} + +// +// Calculates size of card from CSD +// (extension by Martin Thomas, inspired by code from Holger Klabunde) +// +int mscblockGetSize (U32 *pdwDriveSize) +{ + U8 cardresp, i, by; + U8 iob [16]; + U16 c_size, c_size_mult, read_bl_len; + + mscblockCommand (CMD_READCSD, 0); + + do { + cardresp = mscblockResp8b (); + } + while (cardresp != 0xfe); + + DBG ("CSD:"); + + for (i = 0; i < 16; i++) + { + iob [i] = mscspiTransferByte (0xff); + DBG (" %02x", iob [i]); + } + + DBG("\n"); + + mscspiTransferByte (0xff); + mscspiTransferByte (0xff); + + c_size = iob [6] & 0x03; + c_size <<= 10; + c_size += (U16) iob [7] << 2; + c_size += iob [8] >> 6; + + by = iob [5] & 0x0f; + read_bl_len = 1 << by; + + by = iob [9] & 0x03; + by <<= 1; + by += iob [10] >> 7; + + c_size_mult = 1 << (2 + by); + + *pdwDriveSize = (U32) (c_size + 1) * (U32) c_size_mult * (U32) read_bl_len; + + return 0; +} + +// +// +// +static U16 mscblockResp16b (void) +{ + U16 resp; + + resp = (mscblockResp8b() << 8) & 0xff00; + resp |= mscspiTransferByte(0xff); + + return resp; +} + +// +// +// +static int mscblockState (void) +{ + U16 value; + + mscblockCommand (CMD_SENDSTATUS, 0); + value = mscblockResp16b (); + + switch (value) + { + case 0x0000 : return 1; + case 0x0001 : DBG ("Card is Locked.\n"); break; + case 0x0002 : DBG ("WP Erase Skip, Lock/Unlock Cmd Failed.\n"); break; + case 0x0004 : DBG ("General / Unknown error -- card broken?.\n"); break; + case 0x0008 : DBG ("Internal card controller error.\n"); break; + case 0x0010 : DBG ("Card internal ECC was applied, but failed to correct the data.\n"); break; + case 0x0020 : DBG ("Write protect violation.\n"); break; + case 0x0040 : DBG ("An invalid selection, sectors for erase.\n"); break; + case 0x0080 : DBG ("Out of Range, CSD_Overwrite.\n"); break; + default : if (value > 0x00ff) + mscblockResp8bError ((U8) (value >> 8)); + else + DBG ("Unknown error: 0x%x (see SanDisk docs p5-14).\n", value); + break; + } + + return -1; +} + +// +// +// +int mscblockInit (void) +{ + FRESULT f; + + // + // We'll see the error because it means we *must* be using a serial + // console, since we can't use USB for MSC and VCOM at the same time. + // + mscspiInit (); + +#if 1 + if ((f = diskInitialize (0)) != FR_OK) + f_printerror (f); +#endif + +#if 0 + U8 resp; + int i; + + // + // Try to send reset command up to 100 times + // + i = 100; + + do + { + mscblockCommand (CMD_GOIDLESTATE, 0); + resp = mscblockResp8b (); + } + while (resp != 1 && i--); + + if (resp != 1) + { + if (resp == 0xff) + { + DBG ("resp=0xff\n"); + return -1; + } + else + { + mscblockResp8bError (resp); + DBG ("resp!=0xff\n"); + return -2; + } + } + + // + // Wait till card is ready initialising (returns 0 on CMD_1) + // Try up to 32000 times. */ + // + i = 32000; + + do + { + mscblockCommand (CMD_SENDOPCOND, 0); + + if ((resp = mscblockResp8b ()) != 0) + mscblockResp8bError (resp); + } + while (resp == 1 && i--); + + if (resp != 0) + { + mscblockResp8bError (resp); + return -3; + } + + // + // Increase speed after init + // + // mscspiSetSpeed (SPI_PRESCALE_MIN); +#endif + + if (mscblockState () < 0) + { + DBG ("Card didn't return the ready state, breaking up...\n"); + return -2; + } + + DBG ("Init done...\n"); + + return 0; +} + +// +// WAIT ?? -- FIXME +// CMD_WRITE +// WAIT +// CARD RESP +// WAIT +// DATA BLOCK OUT +// START BLOCK +// DATA +// CHKS (2B) +// BUSY... +// +int mscblockWrite (U32 dwAddress, U8 * pbBuf) +{ + U32 place; + U16 t = 0; + + place = 512 * dwAddress; + mscblockCommand (CMD_WRITE, place); + + mscblockResp8b (); /* Card response */ + + mscspiTransferByte (0xfe); /* Start block */ + mscspiSendBlock (pbBuf, 512); + mscspiTransferByte (0xff); /* Checksum part 1 */ + mscspiTransferByte (0xff); /* Checksum part 2 */ + + mscspiTransferByte (0xff); + + while (spiTransferByte (0xff) != 0xff) + t++; + + return 0; +} + +// +// WAIT ?? -- FIXME +// CMD_CMD_ +// WAIT +// CARD RESP +// WAIT +// DATA BLOCK IN +// START BLOCK +// DATA +// CHKS (2B) +// +int mscblockRead (U32 dwAddress, U8 * pbBuf) +{ + U8 cardresp; + U8 firstblock; + U16 fb_timeout = 0xffff; + U32 place; + + place = 512 * dwAddress; + mscblockCommand (CMD_READSINGLEBLOCK, place); + + cardresp = mscblockResp8b (); + + // + // Wait for startblock + // + do + { + firstblock = mscblockResp8b(); + } + while (firstblock == 0xff && fb_timeout--); + + if (cardresp != 0x00 || firstblock != 0xfe) + { + mscblockResp8bError (firstblock); + return -1; + } + + mscspiReceiveBlock (pbBuf, 512); + + // + // Checksum (2 byte) - ignore for now + // + mscspiTransferByte (0xff); + mscspiTransferByte (0xff); + + return 0; +} diff --git a/usbmass/mscblock.h b/usbmass/mscblock.h new file mode 100644 index 0000000..fda5109 --- /dev/null +++ b/usbmass/mscblock.h @@ -0,0 +1,39 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _MSCBLOCK_H_ +#define _MSCBLOCK_H_ + +#include "sysdefs.h" + +int mscblockInit (void); +int mscblockWrite (U32 dwAddress, U8* pbBuf); +int mscblockRead (U32 dwAddress, U8* pbBuf); +int mscblockGetSize (U32 *pdwDriveSize); +int mscblockGetStatus (void); + +#endif diff --git a/usbmass/mscbot.c b/usbmass/mscbot.c new file mode 100644 index 0000000..e813eef --- /dev/null +++ b/usbmass/mscbot.c @@ -0,0 +1,443 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** @file + + Bulk-only-transfer layer for mass storage. + + This layers sits between the generic USB layers and the SCSI layer + and performs data transfer according to the BOT protocol. +*/ + +#include "FreeRTOS.h" + +#include + +#include "../usb/usbapi.h" + +#include "mscdebug.h" +#include "mscbot.h" +#include "mscscsi.h" + +// +// Command block wrapper structure +// +typedef struct +{ + U32 dwCBWSignature; + U32 dwCBWTag; + U32 dwCBWDataTransferLength; + U8 bmCBWFlags; + U8 bCBWLun; + U8 bCBWCBLength; + U8 CBWCB[16]; +} +TCBW; + +// +// Command status wrapper structure +// +typedef struct +{ + U32 dwCSWSignature; + U32 dwCSWTag; + U32 dwCSWDataResidue; + U8 bmCSWStatus; +} +TCSW; + +// +// States of BOT state machine +// +typedef enum +{ + eCBW, + eDataOut, + eDataIn, + eCSW, + eStalled +} +EBotState; + +// +// +// +#define CBW_SIGNATURE 0x43425355 /**< magic word in CBW */ +#define CSW_SIGNATURE 0x53425355 /**< magic word in CSW */ + +#define STATUS_PASSED 0x00 /**< successful transfer */ +#define STATUS_FAILED 0x01 /**< failed transfer */ +#define STATUS_PHASE_ERR 0x02 /**< conflict between host and device */ + +static U32 dwTransferSize; /**< total size of data transfer */ +static U32 dwOffset; /**< offset in current data transfer */ +static TCBW CBW; +static TCSW CSW; +static EBotState eState; +static U8 *pbData; + +// +// Prepares a CSW, to be sent on next bulk-IN interrupt +// +// @param [in] bStatus CSW status +// +static void mscbotSendCSW (U8 bStatus) +{ + int iResidue; + + iResidue = CBW.dwCBWDataTransferLength - dwTransferSize; + + // construct CSW + CSW.dwCSWSignature = CSW_SIGNATURE; + CSW.dwCSWTag = CBW.dwCBWTag; + CSW.dwCSWDataResidue = MAX (iResidue, 0); + CSW.bmCSWStatus = bStatus; + + DBG ("CSW: status=%x, residue=%d\n", bStatus, CSW.dwCSWDataResidue); + + // next state + eState = eCSW; +} + +// +// Checks if CBW is valid and meaningful +// +// @param [in] pCBW Command block wrapper +// @param [in] iLen Length of CBW +// +// @return TRUE if valid and meaningful +// +static BOOL mscbotCheckCBW (TCBW *pCBW, int iLen) +{ + // + // CBW valid? + // + if (iLen != 31) + { + DBG ("Invalid length (%d)\n", iLen); + return FALSE; + } + + if (pCBW->dwCBWSignature != CBW_SIGNATURE) + { + DBG ("Invalid signature %x\n", pCBW->dwCBWSignature); + return FALSE; + } + + // + // CBW meaningful? + // + if (pCBW->bCBWLun != 0) + { + DBG ("Invalid LUN %d\n", pCBW->bCBWLun); + return FALSE; + } + + if ((pCBW->bCBWCBLength < 1) || (pCBW->bCBWCBLength > 16)) + { + DBG ("Invalid CB len %d\n", pCBW->bCBWCBLength); + return FALSE; + } + + return TRUE; +} + +// +// mscbotBOTStall +// Local function to stall ongoing transfer +// +// Which endpoint to stall is determined by looking at the transfer +// direction intended by the host. +// +static void mscbotBOTStall (void) +{ + if ((CBW.bmCBWFlags & 0x80) || (CBW.dwCBWDataTransferLength == 0)) + usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE); + else + usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE); +} + +// +// mscbotHandleDataIn +// Handles data from device-to-host +// +static void mscbotHandleDataIn (void) +{ + int iChunk; + + // + // Process data for host in SCSI layer + // + if ((pbData = mscscsiHandleData (CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset)) == NULL) + { + mscbotBOTStall (); + mscbotSendCSW (STATUS_FAILED); + return; + } + + // + // Send data to host? + // + if (dwOffset < dwTransferSize) + { + iChunk = MIN (64, dwTransferSize - dwOffset); + usbHardwareEndpointWrite (MSC_BULK_IN_EP, pbData, iChunk); + dwOffset += iChunk; + } + + // + // are we done now? + // + if (dwOffset == dwTransferSize) + { + if (dwOffset != CBW.dwCBWDataTransferLength) + { + DBG ("stalling DIN"); + mscbotBOTStall (); + } + + // + // Done + // + mscbotSendCSW (STATUS_PASSED); + } +} + +// +// mscbotHandleDataOut +// Handles data from host-to-device +// +static void mscbotHandleDataOut (void) +{ + int iChunk; + + if (dwOffset < dwTransferSize) + { + iChunk = usbHardwareEndpointRead (MSC_BULK_OUT_EP, pbData, dwTransferSize - dwOffset); + pbData = mscscsiHandleData (CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset); + + if (pbData == NULL) + { + mscbotBOTStall (); + mscbotSendCSW (STATUS_FAILED); + return; + } + + dwOffset += iChunk; + } + + // + // Are we done now? + // + if (dwOffset == dwTransferSize) + { + if (dwOffset != CBW.dwCBWDataTransferLength) + { + DBG ("stalling DOUT"); + mscbotBOTStall (); + } + + mscbotSendCSW (STATUS_PASSED); + } +} + +// +// Resets the BOT state machine +// +void mscbotReset (void) +{ + DBG ("BOT reset in state %d\n", eState); + eState = eCBW; + mscscsiReset (); +} + +// +// Handles the BOT bulk OUT endpoint +// +// @param [in] bEP Endpoint number +// @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc) +// +int mscbotBulkOut (U8 bEP, U8 bEPStatus) +{ + unsigned int iLen, iChunk; + BOOL fHostIn, fDevIn; + + // + // Ignore events on stalled EP + // + if (bEPStatus & EP_STATUS_STALLED) + return 0; + + switch (eState) + { + case eCBW: + { + iLen = usbHardwareEndpointRead (bEP, (U8 *)&CBW, sizeof(CBW)); + + if (!mscbotCheckCBW(&CBW, iLen)) + { + usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE); + usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE); + eState = eStalled; + break; + } + + DBG ("CBW: len=%d, flags=%x, cmd=%x, cmdlen=%d\n", CBW.dwCBWDataTransferLength, CBW.bmCBWFlags, CBW.CBWCB[0], CBW.bCBWCBLength); + + dwOffset = 0; + dwTransferSize = 0; + fHostIn = ((CBW.bmCBWFlags & 0x80) != 0); + + // + // Verify request. Unknown command if NULL + // + if ((pbData = mscscsiHandleCmd (CBW.CBWCB, CBW.bCBWCBLength, &iLen, &fDevIn)) == NULL) + { + mscbotBOTStall (); + mscbotSendCSW (STATUS_FAILED); + break; + } + + // + // Rule: if device and host disagree on direction, send CSW with status 2 + // + if ((iLen > 0) && ((fHostIn && !fDevIn) || (!fHostIn && fDevIn))) + { + DBG ("Host and device disagree on direction\n"); + mscbotBOTStall (); + mscbotSendCSW (STATUS_PHASE_ERR); + break; + } + + // + // Rule: if D > H, send CSW with status 2 + // + if (iLen > CBW.dwCBWDataTransferLength) + { + DBG ("Negative residue\n"); + mscbotBOTStall (); + mscbotSendCSW (STATUS_PHASE_ERR); + break; + } + + if (((dwTransferSize = iLen) == 0) || fDevIn) + { + // + // Data from device-to-host + // + eState = eDataIn; + mscbotHandleDataIn (); + } + else + { + // + // Data from host-to-device + // + eState = eDataOut; + } + } + break; + + case eDataOut: + { + mscbotHandleDataOut (); + } + break; + + case eDataIn: + case eCSW: + { + iChunk = usbHardwareEndpointRead (bEP, NULL, 0); + DBG ("Phase error in state %d, %d bytes\n", eState, iChunk); + eState = eCBW; + } + break; + + case eStalled: + { + usbHardwareEndpointStall (MSC_BULK_OUT_EP, TRUE); + } + break; + + default: + { + DBG ("Invalid state %d\n", eState); + ASSERT (FALSE); + } + break; + } + + return 0; +} + +// +// Handles the BOT bulk IN endpoint +// +// @param [in] bEP Endpoint number +// @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc) +// +int mscbotBulkIn (U8 bEP __attribute__ ((unused)), U8 bEPStatus) +{ + if (bEPStatus & EP_STATUS_STALLED) + return 0; + + switch (eState) + { + // + // Ignore possibly old ACKs + // + case eCBW: + case eDataOut: + break; + + case eDataIn: + mscbotHandleDataIn (); + break; + + // + // Wait for an IN token, then send the CSW + // + case eCSW: + usbHardwareEndpointWrite (MSC_BULK_IN_EP, (U8 *)&CSW, 13); + eState = eCBW; + break; + + // + // Keep stalling + // + case eStalled: + usbHardwareEndpointStall (MSC_BULK_IN_EP, TRUE); + break; + + default: + DBG ("Invalid state %d\n", eState); + ASSERT (FALSE); + break; + } + + return 0; +} diff --git a/usbmass/mscbot.h b/usbmass/mscbot.h new file mode 100644 index 0000000..8e301bf --- /dev/null +++ b/usbmass/mscbot.h @@ -0,0 +1,40 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _MSCBOT_H_ +#define _MSCBOT_H_ + +#include "sysdefs.h" + +#define MSC_BULK_OUT_EP 0x02 +#define MSC_BULK_IN_EP 0x85 + +void mscbotReset (void); +int mscbotBulkOut (U8 bEP, U8 bEPStatus); +int mscbotBulkIn (U8 bEP, U8 bEPStatus); + +#endif diff --git a/usbmass/mscdebug.h b/usbmass/mscdebug.h new file mode 100644 index 0000000..953a523 --- /dev/null +++ b/usbmass/mscdebug.h @@ -0,0 +1,41 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _USBDEBUG_H_ +#define _USBDEBUG_H_ + +#include + +#ifdef USBDEBUG +#define DBG printf +#define ASSERT(x) if(!(x)){DBG("\nAssertion '%s' failed in %s:%s#%d!\n",#x,__FILE__,__FUNCTION__,__LINE__);while(1);} +#else +#define DBG(x ...) do { } while (0) +#define ASSERT(x) do { } while (0) +#endif + +#endif diff --git a/usbmass/mscscsi.c b/usbmass/mscscsi.c new file mode 100644 index 0000000..1690b55 --- /dev/null +++ b/usbmass/mscscsi.c @@ -0,0 +1,334 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + @file + + This is the SCSI layer of the USB mass storage application example. + This layer depends directly on the blockdev layer. + + Windows peculiarities: + * Size of REQUEST SENSE CDB is 12 bytes instead of expected 6 + * Windows requires VERIFY(10) command to do a format. + This command is not mandatory in the SBC/SBC-2 specification. +*/ + +#include "FreeRTOS.h" + +#include // memcpy + +#include "mscdebug.h" +#include "mscblock.h" +#include "mscscsi.h" + +// +// +// +#define BLOCKSIZE 512 + +// +// SBC2 mandatory SCSI commands +// +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_FORMAT_UNIT 0x04 +#define SCSI_CMD_READ_6 0x08 /* not implemented yet */ +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D /* not implemented yet */ +#define SCSI_CMD_READ_CAPACITY_10 0x25 +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_REPORT_LUNS 0xa0 /* not implemented yet */ + +// +// SBC2 optional SCSI commands +// +#define SCSI_CMD_WRITE_6 0x0a /* not implemented yet */ +#define SCSI_CMD_WRITE_10 0x2a +#define SCSI_CMD_VERIFY_10 0x2f /* required for windows format */ + +// +// Sense codes +// +#define WRITE_ERROR 0x030C00 +#define READ_ERROR 0x031100 +#define INVALID_CMD_OPCODE 0x052000 +#define INVALID_FIELD_IN_CDB 0x052400 + +// +// Sense code, which is set on error conditions +// Hex: 00aabbcc, where aa=KEY, bb=ASC, cc=ASCQ +// +static U32 dwSense; + +static const U8 abInquiry[] = +{ + 0x00, // PDT = direct-access device + 0x80, // removeable medium bit = set + 0x05, // version = complies to SPC3 + 0x02, // response data format = SPC3 + 0x1F, // additional length + 0x00, + 0x00, + 0x00, + 'L','P','C','U','S','B',' ',' ', // vendor + 'M','a','s','s',' ','s','t','o', // product + 'r','a','g','e',' ',' ',' ',' ', + '0','.','1',' ' // revision +}; + +// +// Data for "request sense" command. The 0xFF are filled in later +// +static const U8 abSense[] = { 0x70, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00 }; + +// +// Buffer for holding one block of disk data +// +static U8 abBlockBuf [512]; + +typedef struct +{ + U8 bOperationCode; + U8 abLBA [3]; + U8 bLength; + U8 bControl; +} +TCDB6; + +// +// +// +void mscscsiReset (void) +{ + dwSense = 0; +} + +// +// SCSIHandleCmd +// Verifies a SCSI CDB and indicates the direction and amount of data +// that the device wants to transfer. +// +// If this call fails, a sense code is set in dwSense. +// +// IN pbCDB - Command data block +// iCDBLen - Command data block len +// OUT *piRspLen - Length of intended response data: +// *pfDevIn - TRUE if data is transferred from device-to-host +// +// Returns a pointer to the data exchange buffer if successful, +// return NULL otherwise. +// +U8 *mscscsiHandleCmd (U8 *pbCDB, U8 iCDBLen, unsigned int *piRspLen, BOOL *pfDevIn) +{ + static const U8 aiCDBLen [] = {6, 10, 10, 0, 16, 12, 0, 0}; + int i; + TCDB6 *pCDB; + U32 dwLen, dwLBA; + U8 bGroupCode; + + pCDB = (TCDB6 *) pbCDB; + + // default direction is from device to host + *pfDevIn = TRUE; + + // check CDB length + bGroupCode = (pCDB->bOperationCode >> 5) & 0x7; + + if (iCDBLen < aiCDBLen[bGroupCode]) + { + DBG("Invalid CBD len (expected %d)!\n", aiCDBLen[bGroupCode]); + return NULL; + } + + switch (pCDB->bOperationCode) + { + case SCSI_CMD_TEST_UNIT_READY : + DBG ("TEST UNIT READY\n"); + *piRspLen = 0; + break; + + case SCSI_CMD_REQUEST_SENSE : + DBG ("REQUEST SENSE (%06X)\n", dwSense); + *piRspLen = MIN (18, pCDB->bLength); + break; + + case SCSI_CMD_FORMAT_UNIT : + DBG ("FORMAT UNIT %02X\n", pbCDB[1]); + *piRspLen = 0; + break; + + case SCSI_CMD_INQUIRY : + DBG ("INQUIRY\n"); + *piRspLen = MIN (36, pCDB->bLength); + break; + + case SCSI_CMD_READ_CAPACITY_10 : + DBG ("READ CAPACITY\n"); + *piRspLen = 8; + break; + + case SCSI_CMD_READ_10 : + dwLBA = (pbCDB [2] << 24) | (pbCDB [3] << 16) | (pbCDB [4] << 8) | (pbCDB [5]); + dwLen = (pbCDB [7] << 8) | pbCDB [8]; + DBG ("READ10, LBA=%d, len=%d\n", dwLBA, dwLen); + *piRspLen = dwLen * BLOCKSIZE; + break; + + case SCSI_CMD_WRITE_10 : + dwLBA = (pbCDB [2] << 24) | (pbCDB [3] << 16) | (pbCDB [4] << 8) | (pbCDB [5]); + dwLen = (pbCDB [7] << 8) | pbCDB [8]; + DBG ("WRITE10, LBA=%d, len=%d\n", dwLBA, dwLen); + *piRspLen = dwLen * BLOCKSIZE; + *pfDevIn = FALSE; + break; + + case SCSI_CMD_VERIFY_10 : + DBG ("VERIFY10\n"); + if ((pbCDB [1] & (1 << 1)) != 0) + { + DBG ("BYTCHK not supported\n"); + return NULL; + } + *piRspLen = 0; + break; + + default : + DBG ("Unhandled SCSI: "); + for (i = 0; i < iCDBLen; i++) + DBG (" %02X", pbCDB[i]); + DBG ("\n"); + + dwSense = INVALID_CMD_OPCODE; + *piRspLen = 0; + return NULL; + } + + return abBlockBuf; +} + +// +// SCSIHandleData +// Handles a block of SCSI data. +// +// IN pbCDB . Command data block +// iCDBLen - Command data block len +// IN/OUT pbData - Data buffer +// IN dwOffset - Offset in data +// +// Returns a pointer to the next data to be exchanged if successful, +// returns NULL otherwise. +// +U8 *mscscsiHandleData (U8 *pbCDB, U8 iCDBLen __attribute__ ((unused)), U8 *pbData, U32 dwOffset) +{ + TCDB6 *pCDB; + U32 dwLBA; + U32 dwBufPos, dwBlockNr; + U32 dwDevSize, dwMaxBlock; + + pCDB = (TCDB6 *)pbCDB; + + switch (pCDB->bOperationCode) + { + case SCSI_CMD_TEST_UNIT_READY : + if (dwSense != 0) + return NULL; + break; + + case SCSI_CMD_REQUEST_SENSE : + memcpy (pbData, abSense, 18); + pbData [2] = (dwSense >> 16) & 0xff; + pbData [12] = (dwSense >> 8) & 0xff; + pbData [13] = (dwSense >> 0) & 0xff; + dwSense = 0; + break; + + case SCSI_CMD_FORMAT_UNIT : + break; + + case SCSI_CMD_INQUIRY : + memcpy (pbData, abInquiry, sizeof(abInquiry)); + break; + + case SCSI_CMD_READ_CAPACITY_10: + mscblockGetSize (&dwDevSize); + dwMaxBlock = (dwDevSize - 1) / 512; + pbData [0] = (dwMaxBlock >> 24) & 0xff; + pbData [1] = (dwMaxBlock >> 16) & 0xff; + pbData [2] = (dwMaxBlock >> 8) & 0xff; + pbData [3] = (dwMaxBlock >> 0) & 0xff; + pbData [4] = (BLOCKSIZE >> 24) & 0xff; + pbData [5] = (BLOCKSIZE >> 16) & 0xff; + pbData [6] = (BLOCKSIZE >> 8) & 0xff; + pbData [7] = (BLOCKSIZE >> 0) & 0xff; + break; + + case SCSI_CMD_READ_10 : + dwLBA = (pbCDB [2] << 24) | (pbCDB [3] << 16) | (pbCDB [4] << 8) | (pbCDB [5]); + dwBufPos = (dwOffset & (BLOCKSIZE - 1)); + if (dwBufPos == 0) + { + dwBlockNr = dwLBA + (dwOffset / BLOCKSIZE); + DBG ("R"); + if (mscblockRead(dwBlockNr, abBlockBuf) < 0) + { + dwSense = READ_ERROR; + DBG ("mscblockRead failed\n"); + return NULL; + } + } + + return abBlockBuf + dwBufPos ; + + case SCSI_CMD_WRITE_10: + dwLBA = (pbCDB[2] << 24) | (pbCDB[3] << 16) | (pbCDB[4] << 8) | (pbCDB[5]); + + dwBufPos = ((dwOffset + 64) & (BLOCKSIZE - 1)); + if (dwBufPos == 0) + { + dwBlockNr = dwLBA + (dwOffset / BLOCKSIZE); + DBG ("W"); + if (mscblockWrite (dwBlockNr, abBlockBuf) < 0) + { + dwSense = WRITE_ERROR; + DBG ("mscblockWrite failed\n"); + return NULL; + } + } + return abBlockBuf + dwBufPos; + + case SCSI_CMD_VERIFY_10 : + break; + + default : + dwSense = INVALID_CMD_OPCODE; + return NULL; + } + + return abBlockBuf; +} diff --git a/usbmass/mscscsi.h b/usbmass/mscscsi.h new file mode 100644 index 0000000..02046e2 --- /dev/null +++ b/usbmass/mscscsi.h @@ -0,0 +1,37 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _MSCSCSI_H_ +#define _MSCSCSI_H_ + +#include "sysdefs.h" + +void mscscsiReset (void); +U8 *mscscsiHandleCmd (U8 *pbCDB, U8 bCDBLen, unsigned int *piRspLen, BOOL *pfDevIn); +U8 *mscscsiHandleData (U8 *pbCDB, U8 bCDBLen, U8 *pbData, U32 dwOffset); + +#endif diff --git a/usbmass/mscspi.c b/usbmass/mscspi.c new file mode 100644 index 0000000..16f2924 --- /dev/null +++ b/usbmass/mscspi.c @@ -0,0 +1,82 @@ +/*****************************************************************************\ +* efs - General purpose Embedded Filesystem library * +* --------------------- ----------------------------------- * +* * +* Filename : lpc2000_spi.c * +* Description : This contains the functions needed to use efs for * +* accessing files on an SD-card connected to an LPC2xxx. * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation; either * +* version 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* (c)2005 Martin Thomas * +* * +\*****************************************************************************/ + +/* + 2006, Bertrik Sikken, modified for LPCUSB +*/ + +// +// +// +#include "FreeRTOS.h" + +#include "../fatfs/spi.h" + +#include "mscspi.h" + +// +// +// +#define SELECT_CARD() do { GPIO0_IOCLR = GPIO_IO_P20; } while (0) +#define UNSELECT_CARD() do { GPIO0_IOSET = GPIO_IO_P20; } while (0) + +// +// +// +void mscspiInit (void) +{ + spiInit (); +} + +// +// +// +U8 mscspiTransferByte (U8 outgoing) +{ + U8 r; + + spiChipSelect (1); + r = spiTransferByte (outgoing); + spiChipSelect (0); + + return r; +} + +// +// +// +void mscspiSendBlock (U8 *pbBuf, int iLen) +{ + spiChipSelect (1); + spiSendBlock (pbBuf, iLen); + spiChipSelect (0); +} + +// +// +// +void mscspiReceiveBlock (U8 *pbBuf, int iLen) +{ + spiChipSelect (1); + spiReceiveBlock (pbBuf, iLen); + spiChipSelect (0); +} diff --git a/usbmass/mscspi.h b/usbmass/mscspi.h new file mode 100644 index 0000000..5706c63 --- /dev/null +++ b/usbmass/mscspi.h @@ -0,0 +1,49 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _MSCSPI_H_ +#define _MSCSPI_H_ + +// +// +// +#include "sysdefs.h" + +// +// +// +#define SPI_PRESCALE_MIN 8 + +// +// +// +void mscspiInit (void); +U8 mscspiTransferByte (U8 outgoing); +void mscspiSendBlock (U8 *pbBuf, int iLen); +void mscspiReceiveBlock (U8 *pbBuf, int iLen); + +#endif diff --git a/usbmass/mscspi2.c b/usbmass/mscspi2.c new file mode 100644 index 0000000..7b9e20c --- /dev/null +++ b/usbmass/mscspi2.c @@ -0,0 +1,82 @@ +/*****************************************************************************\ +* efs - General purpose Embedded Filesystem library * +* --------------------- ----------------------------------- * +* * +* Filename : lpc2000_spi.c * +* Description : This contains the functions needed to use efs for * +* accessing files on an SD-card connected to an LPC2xxx. * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation; either * +* version 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* (c)2005 Martin Thomas * +* * +\*****************************************************************************/ + +/* + 2006, Bertrik Sikken, modified for LPCUSB +*/ + +// +// +// +#include "FreeRTOS.h" + +#include "../fatfs/spi.h" + +#include "mscspi.h" + +// +// +// +#define SELECT_CARD() do { GPIO0_IOCLR = GPIO_IO_P20; } while (0) +#define UNSELECT_CARD() do { GPIO0_IOSET = GPIO_IO_P20; } while (0) + +// +// +// +void mscspiInit (void) +{ + spiInit (); +} + +// +// +// +U8 mscspiTransferByte (U8 outgoing) +{ + U8 r; + + SELECT_CARD (); + r = spiTransferByte (outgoing); + UNSELECT_CARD (); + + return r; +} + +// +// +// +void mscspiSendBlock (U8 *pbBuf, int iLen) +{ + SELECT_CARD (); + spiSendBlock (pbBuf, iLen); + UNSELECT_CARD (); +} + +// +// +// +void mscspiReceiveBlock (U8 *pbBuf, int iLen) +{ + SELECT_CARD (); + spiReceiveBlock (pbBuf, iLen); + UNSELECT_CARD (); +} diff --git a/usbmass/usbmass.c b/usbmass/usbmass.c new file mode 100644 index 0000000..bb52c74 --- /dev/null +++ b/usbmass/usbmass.c @@ -0,0 +1,205 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "FreeRTOS.h" + +#include "../usb/usbapi.h" + +#include "mscdebug.h" +#include "mscbot.h" +#include "mscblock.h" +#include "usbmass.h" + +// +// +// +#define MAX_PACKET_SIZE 64 +#define LE_WORD(x) ((x)&0xff),((x)>>8) + +// +// +// +static U8 abClassReqData [4]; + +static const U8 abDescriptors [] = +{ + // + // Device descriptor + // + 0x12, + DESC_DEVICE, + LE_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + MAX_PACKET_SIZE0, // bMaxPacketSize + LE_WORD(0xFFFF), // idVendor + LE_WORD(0x0003), // idProduct + LE_WORD(0x0100), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x03, // iSerialNumber + 0x01, // bNumConfigurations + + // + // Configuration descriptor + // + 0x09, + DESC_CONFIGURATION, + LE_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0xC0, // bmAttributes + 0x32, // bMaxPower + + // + // Interface + // + 0x09, + DESC_INTERFACE, + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndPoints + 0x08, // bInterfaceClass = mass storage + 0x06, // bInterfaceSubClass = transparent SCSI + 0x50, // bInterfaceProtocol = BOT + 0x00, // iInterface + + // + // EP + // + 0x07, + DESC_ENDPOINT, + MSC_BULK_IN_EP, // bEndpointAddress + 0x02, // bmAttributes = bulk + LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, // bInterval + + // + // EP + // + 0x07, + DESC_ENDPOINT, + MSC_BULK_OUT_EP, // bEndpointAddress + 0x02, // bmAttributes = bulk + LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, // bInterval + + // + // String descriptors + // + 0x04, + DESC_STRING, + LE_WORD(0x0409), + + 0x0E, + DESC_STRING, + 'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0, + + 0x12, + DESC_STRING, + 'P', 0, 'r', 0, 'o', 0, 'd', 0, 'u', 0, 'c', 0, 't', 0, 'X', 0, + + 0x1A, + DESC_STRING, + 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0, 'C', 0, 'A', 0, 'F', 0, 'E', 0, + + // terminating zero + 0 +}; + + +// +// +// +static BOOL HandleClassRequest (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + if (pSetup->wIndex != 0) + { + DBG ("Invalid idx %X\n", pSetup->wIndex); + return FALSE; + } + + if (pSetup->wValue != 0) + { + DBG ("Invalid val %X\n", pSetup->wValue); + return FALSE; + } + + switch (pSetup->bRequest) + { + // + // Get max LUN (always return no LUNs) + // + case 0xfe: + { + *ppbData [0] = 0; + *piLen = 1; + } + break; + + // + // MSC reset + // + case 0xff: + { + if (pSetup->wLength > 0) + return FALSE; + + mscbotReset (); + } + break; + + default: + { + DBG ("Unhandled class\n"); + } + return FALSE; + } + + return TRUE; +} + +// +// +// +void usbmassInit (void) +{ + portENTER_CRITICAL (); + mscblockInit (); + usbRegisterHandlers (); + usbRegisterDescriptors (abDescriptors); + usbRegisterRequestHandler (REQTYPE_TYPE_CLASS, HandleClassRequest, abClassReqData); + usbHardwareRegisterEPIntHandler (MSC_BULK_IN_EP, mscbotBulkIn); + usbHardwareRegisterEPIntHandler (MSC_BULK_OUT_EP, mscbotBulkOut); + usbHardwareNakIntEnable (INACK_BI); + usbSetupInterruptHandler (); + portEXIT_CRITICAL (); + + usbHardwareConnect (TRUE); +} diff --git a/usbmass/usbmass.h b/usbmass/usbmass.h new file mode 100644 index 0000000..2668a58 --- /dev/null +++ b/usbmass/usbmass.h @@ -0,0 +1,6 @@ +#ifndef _USBMASS_H_ +#define _USBMASS_H_ + +void usbmassInit (void); + +#endif diff --git a/usbser/Makefile b/usbser/Makefile new file mode 100644 index 0000000..28f9d4d --- /dev/null +++ b/usbser/Makefile @@ -0,0 +1,25 @@ +SRC_FILES=usbser.c + +# +# Define all object files. +# +ARM_OBJ = $(SRC_FILES:.c=.o) + +.PHONY: all +all: $(ARM_OBJ) + +$(ARM_OBJ) : %.o : %.c Makefile .depend + $(CC) -c $(CFLAGS) $< -o $@ + $(AR) r $(COMMON)/common.a $@ + +# +# The .depend files contains the list of header files that the +# various source files depend on. By doing this, we'll only +# rebuild the .o's that are affected by header files changing. +# +.depend: + $(CC) $(CFLAGS) -M $(SRC_FILES) > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/usbser/usbser.c b/usbser/usbser.c new file mode 100644 index 0000000..8c351d4 --- /dev/null +++ b/usbser/usbser.c @@ -0,0 +1,338 @@ +#include +#include +#include + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "../usb/usbapi.h" +#include "usbser.h" + +// +// +// +#define EOF (-1) + +#define INT_IN_EP 0x81 +#define BULK_OUT_EP 0x05 +#define BULK_IN_EP 0x82 + +#define MAX_PACKET_SIZE 64 + +#define LE_WORD(x) ((x)&0xFF),((x)>>8) + +// CDC definitions +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +#define SET_LINE_CODING 0x20 +#define GET_LINE_CODING 0x21 +#define SET_CONTROL_LINE_STATE 0x22 + +// +// Data structure for GET_LINE_CODING / SET_LINE_CODING class requests +// +typedef struct +{ + U32 dwDTERate; + U8 bCharFormat; + U8 bParityType; + U8 bDataBits; +} +TLineCoding; + +static TLineCoding LineCoding = {115200, 0, 0, 8}; +static U8 abBulkBuf [64]; +static U8 abClassReqData [8]; +static xQueueHandle xRXQueue = NULL; +static xQueueHandle xTXQueue = NULL; +static int usbSerialInitialized = FALSE; + +// +// +// +static const U8 abDescriptors [] = +{ + // + // Device descriptor + // + 0x12, + DESC_DEVICE, + LE_WORD(0x0200), // bcdUSB + 0x02, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + MAX_PACKET_SIZE0, // bMaxPacketSize + LE_WORD(0xFFFF), // idVendor + LE_WORD(0x0005), // idProduct + LE_WORD(0x0100), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x03, // iSerialNumber + 0x01, // bNumConfigurations + + // + // Configuration descriptor + // + 0x09, + DESC_CONFIGURATION, + LE_WORD(67), // wTotalLength + 0x02, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0xC0, // bmAttributes + 0x32, // bMaxPower + + // + // Control class interface + // + 0x09, + DESC_INTERFACE, + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x01, // bNumEndPoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol, linux requires value of 1 for the cdc_acm module + 0x00, // iInterface + + // + // Header functional descriptor + // + 0x05, + CS_INTERFACE, + 0x00, + LE_WORD(0x0110), + + // + // Call management functional descriptor + // + 0x05, + CS_INTERFACE, + 0x01, + 0x01, // bmCapabilities = device handles call management + 0x01, // bDataInterface + + // + // ACM functional descriptor + // + 0x04, + CS_INTERFACE, + 0x02, + 0x02, // bmCapabilities + + // + // Union functional descriptor + // + 0x05, + CS_INTERFACE, + 0x06, + 0x00, // bMasterInterface + 0x01, // bSlaveInterface0 + + // + // Notification EP + // + 0x07, + DESC_ENDPOINT, + INT_IN_EP, // bEndpointAddress + 0x03, // bmAttributes = intr + LE_WORD(8), // wMaxPacketSize + 0x0A, // bInterval + + // + // Data class interface descriptor + // + 0x09, + DESC_INTERFACE, + 0x01, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndPoints + 0x0A, // bInterfaceClass = data + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // + // Data EP OUT + // + 0x07, + DESC_ENDPOINT, + BULK_OUT_EP, // bEndpointAddress + 0x02, // bmAttributes = bulk + LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, // bInterval + + // + // Data EP in + // + 0x07, + DESC_ENDPOINT, + BULK_IN_EP, // bEndpointAddress + 0x02, // bmAttributes = bulk + LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, // bInterval + + // + // String descriptors + // + 0x04, + DESC_STRING, + LE_WORD(0x0409), + + 0x0E, + DESC_STRING, + 'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0, + + 0x14, + DESC_STRING, + 'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0, + + 0x12, + DESC_STRING, + 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0, + + // + // Terminating zero + // + 0 +}; + +// +// Local function to handle incoming bulk data. This needs to copy the incoming data to the VCOM RX queue. +// +static int usbserBulkOut (U8 bEP, U8 bEPStatus __attribute__ ((unused))) +{ + int i; + int iLen; + portBASE_TYPE xTaskWokenByPost = pdFALSE; + + iLen = usbHardwareEndpointRead (bEP, abBulkBuf, sizeof (abBulkBuf)); + + for (i = 0; i < iLen; i++) + xTaskWokenByPost = xQueueSendFromISR (xRXQueue, &abBulkBuf [i], xTaskWokenByPost); + + return xTaskWokenByPost; +} + +// +// Local function to handle outgoing bulk data. This needs to copy any user data in the VCOM TX queue to the bulk endpoint. +// +static int usbserBulkIn (U8 bEP, U8 bEPStatus __attribute__ ((unused))) +{ + int i; + portBASE_TYPE xTaskWoken = pdFALSE; + + for (i = 0; i < MAX_PACKET_SIZE; i++) + if (!xQueueReceiveFromISR (xTXQueue, &abBulkBuf [i], &xTaskWoken)) + break; + + if (i > 0) + usbHardwareEndpointWrite (bEP, abBulkBuf, i); + + return xTaskWoken; +} + +// +// Local function to handle the USB-CDC class requests +// +static BOOL usbserHandleClassRequest (TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + switch (pSetup->bRequest) + { + case SET_LINE_CODING: + { + memcpy ((U8 *) &LineCoding, *ppbData, 7); + *piLen = 7; + } + break; + + case GET_LINE_CODING: + { + *ppbData = (U8 *) &LineCoding; + *piLen = 7; + } + break; + + case SET_CONTROL_LINE_STATE: + break; + + default: + return FALSE; + } + + return TRUE; +} + +// +// Writes one character to VCOM port +// +signed portBASE_TYPE usbserPutChar (signed portCHAR cOutChar, portTickType xBlockTime) +{ + if (!usbSerialInitialized) + return pdFALSE; + + return xQueueSend (xTXQueue, &cOutChar, xBlockTime); +} + +signed portBASE_TYPE usbserPutString (const signed portCHAR * const pcString, portTickType xBlockTime) +{ + signed portCHAR *pxNext; + + if (!usbSerialInitialized) + return pdFALSE; + + pxNext = (signed portCHAR *) pcString; + + while (*pxNext) + { + if (!usbserPutChar (*pxNext, xBlockTime)) + return pdFALSE; + + pxNext++; + } + + return pdTRUE; +} + +// +// Reads one character from VCOM port +// +signed portBASE_TYPE usbserGetChar (signed portCHAR *pcRxedChar, portTickType xBlockTime) +{ + if (!usbSerialInitialized) + return pdFALSE; + + if (xQueueReceive (xRXQueue, pcRxedChar, xBlockTime)) + return pdTRUE; + else + return pdFALSE; +} + +void usbserGetRxQueue (xQueueHandle *qh) +{ + *qh = xRXQueue; +} + +// +// +// +void usbserInit (void) +{ + portENTER_CRITICAL (); + usbRegisterHandlers (); + usbRegisterDescriptors (abDescriptors); + usbRegisterRequestHandler (REQTYPE_TYPE_CLASS, usbserHandleClassRequest, abClassReqData); + usbHardwareRegisterEPIntHandler (INT_IN_EP, NULL); + usbHardwareRegisterEPIntHandler (BULK_IN_EP, usbserBulkIn); + usbHardwareRegisterEPIntHandler (BULK_OUT_EP, usbserBulkOut); + usbHardwareNakIntEnable (INACK_BI); + usbSetupInterruptHandler (); + xRXQueue = xQueueCreate (256, (unsigned portBASE_TYPE) sizeof (signed portCHAR)); + xTXQueue = xQueueCreate (256, (unsigned portBASE_TYPE) sizeof (signed portCHAR)); + usbSerialInitialized = TRUE; + portEXIT_CRITICAL (); + + usbHardwareConnect (TRUE); +} diff --git a/usbser/usbser.h b/usbser/usbser.h new file mode 100644 index 0000000..067402f --- /dev/null +++ b/usbser/usbser.h @@ -0,0 +1,16 @@ +#ifndef _USBSER_H_ +#define _USBSER_H_ + +#include "FreeRTOS.h" +#include "queue.h" + +// +// +// +signed portBASE_TYPE usbserPutChar (signed portCHAR cOutChar, portTickType xBlockTime); +signed portBASE_TYPE usbserPutString (const signed portCHAR * const pcString, portTickType xBlockTime); +signed portBASE_TYPE usbserGetChar (signed portCHAR *pcRxedChar, portTickType xBlockTime); +void usbserInit (void); +void usbserGetRxQueue (xQueueHandle *qh); + +#endif -- 2.30.2