Initial version

This commit is contained in:
2019-06-28 23:08:36 +02:00
commit 4d8973e20b
2426 changed files with 948029 additions and 0 deletions

View File

@ -0,0 +1,226 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
/**
* @brief Size of the shared memory region.
*/
#define SHARED_MEMORY_SIZE 32
/**
* @brief Memory region shared between two tasks.
*/
static uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) );
/**
* @brief Memory region used to track Memory Fault intentionally caused by the
* RO Access task.
*
* RO Access task sets ucROTaskFaultTracker[ 0 ] to 1 before accessing illegal
* memory. Illegal memory access causes Memory Fault and the fault handler
* checks ucROTaskFaultTracker[ 0 ] to see if this is an expected fault. We
* recover gracefully from an expected fault by jumping to the next instruction.
*
* @note We are declaring a region of 32 bytes even though we need only one. The
* reason is that the size of an MPU region must be a multiple of 32 bytes.
*/
static uint8_t ucROTaskFaultTracker[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) ) = { 0 };
/*-----------------------------------------------------------*/
/**
* @brief Implements the task which has Read Only access to the memory region
* ucSharedMemory.
*
* @param pvParameters[in] Parameters as passed during task creation.
*/
static void prvROAccessTask( void * pvParameters );
/**
* @brief Implements the task which has Read Write access to the memory region
* ucSharedMemory.
*
* @param pvParameters[in] Parameters as passed during task creation.
*/
static void prvRWAccessTask( void * pvParameters );
/*-----------------------------------------------------------*/
static void prvROAccessTask( void * pvParameters )
{
uint8_t ucVal;
/* Unused parameters. */
( void ) pvParameters;
for( ; ; )
{
/* This task has RO access to ucSharedMemory and therefore it can read
* it but cannot modify it. */
ucVal = ucSharedMemory[ 0 ];
/* Silent compiler warnings about unused variables. */
( void ) ucVal;
/* Since this task has Read Only access to the ucSharedMemory region,
* writing to it results in Memory Fault. Set ucROTaskFaultTracker[ 0 ]
* to 1 to tell the Memory Fault Handler that this is an expected fault.
* The handler will recover from this fault gracefully by jumping to the
* next instruction. */
ucROTaskFaultTracker[ 0 ] = 1;
/* Illegal access to generate Memory Fault. */
ucSharedMemory[ 0 ] = 0;
/* Wait for a second. */
vTaskDelay( pdMS_TO_TICKS( 1000 ) );
}
}
/*-----------------------------------------------------------*/
static void prvRWAccessTask( void * pvParameters )
{
/* Unused parameters. */
( void ) pvParameters;
for( ; ; )
{
/* This task has RW access to ucSharedMemory and therefore can write to
* it. */
ucSharedMemory[ 0 ] = 0;
/* Wait for a second. */
vTaskDelay( pdMS_TO_TICKS( 1000 ) );
}
}
/*-----------------------------------------------------------*/
void vStartMPUDemo( void )
{
static StackType_t xROAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
static StackType_t xRWAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
TaskParameters_t xROAccessTaskParameters =
{
.pvTaskCode = prvROAccessTask,
.pcName = "ROAccess",
.usStackDepth = configMINIMAL_STACK_SIZE,
.pvParameters = NULL,
.uxPriority = tskIDLE_PRIORITY,
.puxStackBuffer = xROAccessTaskStack,
.xRegions = {
{ ucSharedMemory, 32, tskMPU_REGION_READ_ONLY | tskMPU_REGION_EXECUTE_NEVER },
{ ucROTaskFaultTracker, 32, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
{ 0, 0, 0 },
}
};
TaskParameters_t xRWAccessTaskParameters =
{
.pvTaskCode = prvRWAccessTask,
.pcName = "RWAccess",
.usStackDepth = configMINIMAL_STACK_SIZE,
.pvParameters = NULL,
.uxPriority = tskIDLE_PRIORITY,
.puxStackBuffer = xRWAccessTaskStack,
.xRegions = {
{ ucSharedMemory, 32, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
{ 0, 0, 0 },
{ 0, 0, 0 },
}
};
/* Create an unprivileged task with RO access to ucSharedMemory. */
xTaskCreateRestricted( &( xROAccessTaskParameters ), NULL );
/* Create an unprivileged task with RW access to ucSharedMemory. */
xTaskCreateRestricted( &( xRWAccessTaskParameters ), NULL );
}
/*-----------------------------------------------------------*/
portDONT_DISCARD void vHandleMemoryFault( uint32_t * pulFaultStackAddress )
{
uint32_t ulPC;
uint16_t usOffendingInstruction;
/* Is this an expected fault? */
if( ucROTaskFaultTracker[ 0 ] == 1 )
{
/* Read program counter. */
ulPC = pulFaultStackAddress[ 6 ];
/* Read the offending instruction. */
usOffendingInstruction = *( uint16_t * )ulPC;
/* From ARM docs:
* If the value of bits[15:11] of the halfword being decoded is one of
* the following, the halfword is the first halfword of a 32-bit
* instruction:
* - 0b11101.
* - 0b11110.
* - 0b11111.
* Otherwise, the halfword is a 16-bit instruction.
*/
/* Extract bits[15:11] of the offending instruction. */
usOffendingInstruction = usOffendingInstruction & 0xF800;
usOffendingInstruction = ( usOffendingInstruction >> 11 );
/* Determine if the offending instruction is a 32-bit instruction or
* a 16-bit instruction. */
if( usOffendingInstruction == 0x001F ||
usOffendingInstruction == 0x001E ||
usOffendingInstruction == 0x001D )
{
/* Since the offending instruction is a 32-bit instruction,
* increment the program counter by 4 to move to the next
* instruction. */
ulPC += 4;
}
else
{
/* Since the offending instruction is a 16-bit instruction,
* increment the program counter by 2 to move to the next
* instruction. */
ulPC += 2;
}
/* Save the new program counter on the stack. */
pulFaultStackAddress[ 6 ] = ulPC;
/* Mark the fault as handled. */
ucROTaskFaultTracker[ 0 ] = 0;
}
else
{
/* This is an unexpected fault - loop forever. */
for( ; ; )
{
}
}
}
/*-----------------------------------------------------------*/

View File

@ -0,0 +1,45 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef __MPU_DEMO_H__
#define __MPU_DEMO_H__
/**
* @brief Creates all the tasks for MPU demo.
*
* The MPU demo creates 2 unprivileged tasks - One of which has Read Only access
* to a shared memory region while the other has Read Write access. The task
* with Read Only access then tries to write to the shared memory which results
* in a Memory fault. The fault handler examines that it is the fault generated
* by the task with Read Only access and if so, it recovers from the fault
* greacefully by moving the Program Counter to the next instruction to the one
* which generated the fault. If any other memory access violation occurs, the
* fault handler will get stuck in an inifinite loop.
*/
void vStartMPUDemo( void );
#endif /* __MPU_DEMO_H__ */

View File

@ -0,0 +1,59 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#include <arm_cmse.h>
#include "nsc_functions.h"
#include "secure_port_macros.h"
/**
* @brief Counter returned from NSCFunction.
*/
static uint32_t ulSecureCounter = 0;
/**
* @brief typedef for non-secure callback.
*/
typedef void ( *NonSecureCallback_t ) ( void ) __attribute__( ( cmse_nonsecure_call ) );
/*-----------------------------------------------------------*/
secureportNON_SECURE_CALLABLE uint32_t NSCFunction( Callback_t pxCallback )
{
NonSecureCallback_t pxNonSecureCallback;
/* Return function pointer with cleared LSB. */
pxNonSecureCallback = ( NonSecureCallback_t ) cmse_nsfptr_create( pxCallback );
/* Invoke the supplied callback. */
pxNonSecureCallback();
/* Increment the secure side counter. */
ulSecureCounter += 1;
/* Return the secure side counter. */
return ulSecureCounter;
}
/*-----------------------------------------------------------*/

View File

@ -0,0 +1,51 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef __NSC_FUNCTIONS_H__
#define __NSC_FUNCTIONS_H__
#include <stdint.h>
/**
* @brief Callback function pointer definition.
*/
typedef void ( *Callback_t ) ( void );
/**
* @brief Invokes the supplied callback which is on the non-secure side.
*
* Returns a number which is one more than the value returned in previous
* invocation of this function. Initial invocation returns 1.
*
* @param pxCallback[in] The callback to invoke.
*
* @return A number which is one more than the value returned in previous
* invocation of this function.
*/
uint32_t NSCFunction( Callback_t pxCallback );
#endif /* __NSC_FUNCTIONS_H__ */

View File

@ -0,0 +1,133 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
/* Non-Secure callable functions. */
#include "nsc_functions.h"
/**
* @brief Counter incremented in the callback which is called from the secure
* side.
*
* The size of an MPU region must be a multiple of 32 bytes. Therefore we need
* to declare an array of size 8 to ensure that the total size is 32 bytes -
* even though we only need 4 bytes. If we do not do that, anything placed after
* 4 bytes and upto 32 bytes will also fall in the same MPU region and the task
* having access to ulNonSecureCounter will also have access to all those items.
*/
static uint32_t ulNonSecureCounter[8] __attribute__( ( aligned( 32 ) ) ) = { 0 };
/*-----------------------------------------------------------*/
/**
* @brief Creates all the tasks for TZ demo.
*/
void vStartTZDemo( void );
/**
* @brief Increments the ulNonSecureCounter.
*
* This function is called from the secure side.
*/
static void prvCallback( void );
/**
* @brief Implements the task which calls the functions exported from the secure
* side.
*
* @param pvParameters[in] Parameters as passed during task creation.
*/
static void prvSecureCallingTask( void * pvParameters );
/*-----------------------------------------------------------*/
void vStartTZDemo( void )
{
static StackType_t xSecureCallingTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
TaskParameters_t xSecureCallingTaskParameters =
{
.pvTaskCode = prvSecureCallingTask,
.pcName = "SecCalling",
.usStackDepth = configMINIMAL_STACK_SIZE,
.pvParameters = NULL,
.uxPriority = tskIDLE_PRIORITY,
.puxStackBuffer = xSecureCallingTaskStack,
.xRegions = {
{ ulNonSecureCounter, 32, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
{ 0, 0, 0 },
{ 0, 0, 0 },
}
};
/* Create an unprivileged task which calls secure functions. */
xTaskCreateRestricted( &( xSecureCallingTaskParameters ), NULL );
}
/*-----------------------------------------------------------*/
static void prvCallback( void )
{
/* This function is called from the secure side. Just increment the counter
* here. The check that this counter keeps incrementing is performed in the
* prvSecureCallingTask. */
ulNonSecureCounter[ 0 ] += 1;
}
/*-----------------------------------------------------------*/
static void prvSecureCallingTask( void * pvParameters )
{
uint32_t ulLastSecureCounter = 0, ulLastNonSecureCounter = 0;
uint32_t ulCurrentSecureCounter = 0;
/* This task calls secure side functions. So allocate a secure context for
* it. */
portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE );
for( ; ; )
{
/* Call the secure side function. It does two things:
* - It calls the supplied function (prvCallback) which in turn
* increments the non-secure counter.
* - It increments the secure counter and returns the incremented value.
* Therefore at the end of this function call both the secure and
* non-secure counters must have been incremented.
*/
ulCurrentSecureCounter = NSCFunction( prvCallback );
/* Make sure that both the counters are incremented. */
configASSERT( ulCurrentSecureCounter == ulLastSecureCounter + 1 );
configASSERT( ulNonSecureCounter[ 0 ] == ulLastNonSecureCounter + 1 );
/* Update the last values for both the counters. */
ulLastSecureCounter = ulCurrentSecureCounter;
ulLastNonSecureCounter = ulNonSecureCounter[ 0 ];
/* Wait for a second. */
vTaskDelay( pdMS_TO_TICKS( 1000 ) );
}
}
/*-----------------------------------------------------------*/

View File

@ -0,0 +1,45 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef __TZ_DEMO_H__
#define __TZ_DEMO_H__
/**
* @brief Creates all the tasks for TZ demo.
*
* The Trust Zone (TZ) demo creates an unprivileged task which calls a secure
* side function and passes a pointer to a callback function. The secure side
* function does two things:
* 1. It calls the provided callback function. The callback function increments
* a counter.
* 2. It increments a counter and returns the incremented value.
* After the secure function call finishes, it verifies that both the counters
* are incremented.
*/
void vStartTZDemo( void );
#endif /* __TZ_DEMO_H__ */

View File

@ -0,0 +1,308 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Creates six tasks that operate on three queues as follows:
*
* The first two tasks send and receive an incrementing number to/from a queue.
* One task acts as a producer and the other as the consumer. The consumer is a
* higher priority than the producer and is set to block on queue reads. The queue
* only has space for one item - as soon as the producer posts a message on the
* queue the consumer will unblock, pre-empt the producer, and remove the item.
*
* The second two tasks work the other way around. Again the queue used only has
* enough space for one item. This time the consumer has a lower priority than the
* producer. The producer will try to post on the queue blocking when the queue is
* full. When the consumer wakes it will remove the item from the queue, causing
* the producer to unblock, pre-empt the consumer, and immediately re-fill the
* queue.
*
* The last two tasks use the same queue producer and consumer functions. This time the queue has
* enough space for lots of items and the tasks operate at the same priority. The
* producer will execute, placing items into the queue. The consumer will start
* executing when either the queue becomes full (causing the producer to block) or
* a context switch occurs (tasks of the same priority will time slice).
*
* \page BlockQC blockQ.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V1.00:
+ Reversed the priority and block times of the second two demo tasks so
they operate as per the description above.
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
Changes from V4.0.2
+ The second set of tasks were created the wrong way around. This has been
corrected.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo program include files. */
#include "BlockQ.h"
#include "print.h"
#define blckqSTACK_SIZE ( ( unsigned short ) configMINIMAL_STACK_SIZE )
#define blckqNUM_TASK_SETS ( 3 )
/* Structure used to pass parameters to the blocking queue tasks. */
typedef struct BLOCKING_QUEUE_PARAMETERS
{
QueueHandle_t xQueue; /*< The queue to be used by the task. */
TickType_t xBlockTime; /*< The block time to use on queue reads/writes. */
volatile short *psCheckVariable; /*< Incremented on each successful cycle to check the task is still running. */
} xBlockingQueueParameters;
/* Task function that creates an incrementing number and posts it on a queue. */
static void vBlockingQueueProducer( void *pvParameters );
/* Task function that removes the incrementing number from a queue and checks that
it is the expected number. */
static void vBlockingQueueConsumer( void *pvParameters );
/* Variables which are incremented each time an item is removed from a queue, and
found to be the expected value.
These are used to check that the tasks are still running. */
static volatile short sBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 };
/* Variable which are incremented each time an item is posted on a queue. These
are used to check that the tasks are still running. */
static volatile short sBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 };
/*-----------------------------------------------------------*/
void vStartBlockingQueueTasks( unsigned portBASE_TYPE uxPriority )
{
xBlockingQueueParameters *pxQueueParameters1, *pxQueueParameters2;
xBlockingQueueParameters *pxQueueParameters3, *pxQueueParameters4;
xBlockingQueueParameters *pxQueueParameters5, *pxQueueParameters6;
const unsigned portBASE_TYPE uxQueueSize1 = 1, uxQueueSize5 = 5;
const TickType_t xBlockTime = ( TickType_t ) 1000 / portTICK_PERIOD_MS;
const TickType_t xDontBlock = ( TickType_t ) 0;
/* Create the first two tasks as described at the top of the file. */
/* First create the structure used to pass parameters to the consumer tasks. */
pxQueueParameters1 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
/* Create the queue used by the first two tasks to pass the incrementing number.
Pass a pointer to the queue in the parameter structure. */
pxQueueParameters1->xQueue = xQueueCreate( uxQueueSize1, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) );
/* The consumer is created first so gets a block time as described above. */
pxQueueParameters1->xBlockTime = xBlockTime;
/* Pass in the variable that this task is going to increment so we can check it
is still running. */
pxQueueParameters1->psCheckVariable = &( sBlockingConsumerCount[ 0 ] );
/* Create the structure used to pass parameters to the producer task. */
pxQueueParameters2 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
/* Pass the queue to this task also, using the parameter structure. */
pxQueueParameters2->xQueue = pxQueueParameters1->xQueue;
/* The producer is not going to block - as soon as it posts the consumer will
wake and remove the item so the producer should always have room to post. */
pxQueueParameters2->xBlockTime = xDontBlock;
/* Pass in the variable that this task is going to increment so we can check
it is still running. */
pxQueueParameters2->psCheckVariable = &( sBlockingProducerCount[ 0 ] );
/* Note the producer has a lower priority than the consumer when the tasks are
spawned. */
xTaskCreate( vBlockingQueueConsumer, "QConsB1", blckqSTACK_SIZE, ( void * ) pxQueueParameters1, uxPriority, NULL );
xTaskCreate( vBlockingQueueProducer, "QProdB2", blckqSTACK_SIZE, ( void * ) pxQueueParameters2, tskIDLE_PRIORITY, NULL );
/* Create the second two tasks as described at the top of the file. This uses
the same mechanism but reverses the task priorities. */
pxQueueParameters3 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters3->xQueue = xQueueCreate( uxQueueSize1, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) );
pxQueueParameters3->xBlockTime = xDontBlock;
pxQueueParameters3->psCheckVariable = &( sBlockingProducerCount[ 1 ] );
pxQueueParameters4 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters4->xQueue = pxQueueParameters3->xQueue;
pxQueueParameters4->xBlockTime = xBlockTime;
pxQueueParameters4->psCheckVariable = &( sBlockingConsumerCount[ 1 ] );
xTaskCreate( vBlockingQueueProducer, "QProdB3", blckqSTACK_SIZE, ( void * ) pxQueueParameters3, tskIDLE_PRIORITY, NULL );
xTaskCreate( vBlockingQueueConsumer, "QConsB4", blckqSTACK_SIZE, ( void * ) pxQueueParameters4, uxPriority, NULL );
/* Create the last two tasks as described above. The mechanism is again just
the same. This time both parameter structures are given a block time. */
pxQueueParameters5 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters5->xQueue = xQueueCreate( uxQueueSize5, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) );
pxQueueParameters5->xBlockTime = xBlockTime;
pxQueueParameters5->psCheckVariable = &( sBlockingProducerCount[ 2 ] );
pxQueueParameters6 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters6->xQueue = pxQueueParameters5->xQueue;
pxQueueParameters6->xBlockTime = xBlockTime;
pxQueueParameters6->psCheckVariable = &( sBlockingConsumerCount[ 2 ] );
xTaskCreate( vBlockingQueueProducer, "QProdB5", blckqSTACK_SIZE, ( void * ) pxQueueParameters5, tskIDLE_PRIORITY, NULL );
xTaskCreate( vBlockingQueueConsumer, "QConsB6", blckqSTACK_SIZE, ( void * ) pxQueueParameters6, tskIDLE_PRIORITY, NULL );
}
/*-----------------------------------------------------------*/
static void vBlockingQueueProducer( void *pvParameters )
{
unsigned short usValue = 0;
xBlockingQueueParameters *pxQueueParameters;
const char * const pcTaskStartMsg = "Blocking queue producer started.\r\n";
const char * const pcTaskErrorMsg = "Could not post on blocking queue\r\n";
short sErrorEverOccurred = pdFALSE;
pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
if( xQueueSendToBack( pxQueueParameters->xQueue, ( void * ) &usValue, pxQueueParameters->xBlockTime ) != pdPASS )
{
vPrintDisplayMessage( &pcTaskErrorMsg );
sErrorEverOccurred = pdTRUE;
}
else
{
/* We have successfully posted a message, so increment the variable
used to check we are still running. */
if( sErrorEverOccurred == pdFALSE )
{
( *pxQueueParameters->psCheckVariable )++;
}
/* Increment the variable we are going to post next time round. The
consumer will expect the numbers to follow in numerical order. */
++usValue;
}
}
}
/*-----------------------------------------------------------*/
static void vBlockingQueueConsumer( void *pvParameters )
{
unsigned short usData, usExpectedValue = 0;
xBlockingQueueParameters *pxQueueParameters;
const char * const pcTaskStartMsg = "Blocking queue consumer started.\r\n";
const char * const pcTaskErrorMsg = "Incorrect value received on blocking queue.\r\n";
short sErrorEverOccurred = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters;
for( ;; )
{
if( xQueueReceive( pxQueueParameters->xQueue, &usData, pxQueueParameters->xBlockTime ) == pdPASS )
{
if( usData != usExpectedValue )
{
vPrintDisplayMessage( &pcTaskErrorMsg );
/* Catch-up. */
usExpectedValue = usData;
sErrorEverOccurred = pdTRUE;
}
else
{
/* We have successfully received a message, so increment the
variable used to check we are still running. */
if( sErrorEverOccurred == pdFALSE )
{
( *pxQueueParameters->psCheckVariable )++;
}
/* Increment the value we expect to remove from the queue next time
round. */
++usExpectedValue;
}
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
portBASE_TYPE xAreBlockingQueuesStillRunning( void )
{
static short sLastBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 };
static short sLastBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 };
portBASE_TYPE xReturn = pdPASS, xTasks;
/* Not too worried about mutual exclusion on these variables as they are 16
bits and we are only reading them. We also only care to see if they have
changed or not.
Loop through each check variable and return pdFALSE if any are found not
to have changed since the last call. */
for( xTasks = 0; xTasks < blckqNUM_TASK_SETS; xTasks++ )
{
if( sBlockingConsumerCount[ xTasks ] == sLastBlockingConsumerCount[ xTasks ] )
{
xReturn = pdFALSE;
}
sLastBlockingConsumerCount[ xTasks ] = sBlockingConsumerCount[ xTasks ];
if( sBlockingProducerCount[ xTasks ] == sLastBlockingProducerCount[ xTasks ] )
{
xReturn = pdFALSE;
}
sLastBlockingProducerCount[ xTasks ] = sBlockingProducerCount[ xTasks ];
}
return xReturn;
}

View File

@ -0,0 +1,220 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* This is a very simple queue test. See the BlockQ. c documentation for a more
* comprehensive version.
*
* Creates two tasks that communicate over a single queue. One task acts as a
* producer, the other a consumer.
*
* The producer loops for three iteration, posting an incrementing number onto the
* queue each cycle. It then delays for a fixed period before doing exactly the
* same again.
*
* The consumer loops emptying the queue. Each item removed from the queue is
* checked to ensure it contains the expected value. When the queue is empty it
* blocks for a fixed period, then does the same again.
*
* All queue access is performed without blocking. The consumer completely empties
* the queue each time it runs so the producer should never find the queue full.
*
* An error is flagged if the consumer obtains an unexpected value or the producer
* find the queue is full.
*
* \page PollQC pollQ.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "print.h"
/* Demo program include files. */
#include "PollQ.h"
#define pollqSTACK_SIZE ( ( unsigned short ) configMINIMAL_STACK_SIZE )
/* The task that posts the incrementing number onto the queue. */
static void vPolledQueueProducer( void *pvParameters );
/* The task that empties the queue. */
static void vPolledQueueConsumer( void *pvParameters );
/* Variables that are used to check that the tasks are still running with no errors. */
static volatile short sPollingConsumerCount = 0, sPollingProducerCount = 0;
/*-----------------------------------------------------------*/
void vStartPolledQueueTasks( unsigned portBASE_TYPE uxPriority )
{
static QueueHandle_t xPolledQueue;
const unsigned portBASE_TYPE uxQueueSize = 10;
/* Create the queue used by the producer and consumer. */
xPolledQueue = xQueueCreate( uxQueueSize, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) );
/* Spawn the producer and consumer. */
xTaskCreate( vPolledQueueConsumer, "QConsNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, NULL );
xTaskCreate( vPolledQueueProducer, "QProdNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, NULL );
}
/*-----------------------------------------------------------*/
static void vPolledQueueProducer( void *pvParameters )
{
unsigned short usValue = 0, usLoop;
QueueHandle_t *pxQueue;
const TickType_t xDelay = ( TickType_t ) 200 / portTICK_PERIOD_MS;
const unsigned short usNumToProduce = 3;
const char * const pcTaskStartMsg = "Polled queue producer started.\r\n";
const char * const pcTaskErrorMsg = "Could not post on polled queue.\r\n";
short sError = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The queue being used is passed in as the parameter. */
pxQueue = ( QueueHandle_t * ) pvParameters;
for( ;; )
{
for( usLoop = 0; usLoop < usNumToProduce; ++usLoop )
{
/* Send an incrementing number on the queue without blocking. */
if( xQueueSendToBack( *pxQueue, ( void * ) &usValue, ( TickType_t ) 0 ) != pdPASS )
{
/* We should never find the queue full - this is an error. */
vPrintDisplayMessage( &pcTaskErrorMsg );
sError = pdTRUE;
}
else
{
if( sError == pdFALSE )
{
/* If an error has ever been recorded we stop incrementing the
check variable. */
++sPollingProducerCount;
}
/* Update the value we are going to post next time around. */
++usValue;
}
}
/* Wait before we start posting again to ensure the consumer runs and
empties the queue. */
vTaskDelay( xDelay );
}
}
/*-----------------------------------------------------------*/
static void vPolledQueueConsumer( void *pvParameters )
{
unsigned short usData, usExpectedValue = 0;
QueueHandle_t *pxQueue;
const TickType_t xDelay = ( TickType_t ) 200 / portTICK_PERIOD_MS;
const char * const pcTaskStartMsg = "Polled queue consumer started.\r\n";
const char * const pcTaskErrorMsg = "Incorrect value received on polled queue.\r\n";
short sError = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The queue being used is passed in as the parameter. */
pxQueue = ( QueueHandle_t * ) pvParameters;
for( ;; )
{
/* Loop until the queue is empty. */
while( uxQueueMessagesWaiting( *pxQueue ) )
{
if( xQueueReceive( *pxQueue, &usData, ( TickType_t ) 0 ) == pdPASS )
{
if( usData != usExpectedValue )
{
/* This is not what we expected to receive so an error has
occurred. */
vPrintDisplayMessage( &pcTaskErrorMsg );
sError = pdTRUE;
/* Catch-up to the value we received so our next expected value
should again be correct. */
usExpectedValue = usData;
}
else
{
if( sError == pdFALSE )
{
/* Only increment the check variable if no errors have
occurred. */
++sPollingConsumerCount;
}
}
++usExpectedValue;
}
}
/* Now the queue is empty we block, allowing the producer to place more
items in the queue. */
vTaskDelay( xDelay );
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running with no errors. */
portBASE_TYPE xArePollingQueuesStillRunning( void )
{
static short sLastPollingConsumerCount = 0, sLastPollingProducerCount = 0;
portBASE_TYPE xReturn;
if( ( sLastPollingConsumerCount == sPollingConsumerCount ) ||
( sLastPollingProducerCount == sPollingProducerCount )
)
{
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
sLastPollingConsumerCount = sPollingConsumerCount;
sLastPollingProducerCount = sPollingProducerCount;
return xReturn;
}

View File

@ -0,0 +1,346 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Creates two tasks that operate on an interrupt driven serial port. A loopback
* connector should be used so that everything that is transmitted is also received.
* The serial port does not use any flow control. On a standard 9way 'D' connector
* pins two and three should be connected together.
*
* The first task repeatedly sends a string to a queue, character at a time. The
* serial port interrupt will empty the queue and transmit the characters. The
* task blocks for a pseudo random period before resending the string.
*
* The second task blocks on a queue waiting for a character to be received.
* Characters received by the serial port interrupt routine are posted onto the
* queue - unblocking the task making it ready to execute. If this is then the
* highest priority task ready to run it will run immediately - with a context
* switch occurring at the end of the interrupt service routine. The task
* receiving characters is spawned with a higher priority than the task
* transmitting the characters.
*
* With the loop back connector in place, one task will transmit a string and the
* other will immediately receive it. The receiving task knows the string it
* expects to receive so can detect an error.
*
* This also creates a third task. This is used to test semaphore usage from an
* ISR and does nothing interesting.
*
* \page ComTestC comtest.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V1.00:
+ The priority of the Rx task has been lowered. Received characters are
now processed (read from the queue) at the idle priority, allowing low
priority tasks to run evenly at times of a high communications overhead.
Changes from V1.01:
+ The Tx task now waits a pseudo random time between transissions.
Previously a fixed period was used but this was not such a good test as
interrupts fired at regular intervals.
Changes From V1.2.0:
+ Use vSerialPutString() instead of single character puts.
+ Only stop the check variable incrementing after two consecutive errors.
Changed from V1.2.5
+ Made the Rx task 2 priorities higher than the Tx task. Previously it was
only 1. This is done to tie in better with the other demo application
tasks.
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
+ Slight modification to task priorities.
*/
/* Scheduler include files. */
#include <stdlib.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "serial.h"
#include "comtest.h"
#include "print.h"
/* The Tx task will transmit the sequence of characters at a pseudo random
interval. This is the maximum and minimum block time between sends. */
#define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x15e )
#define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0xc8 )
#define comMAX_CONSECUTIVE_ERRORS ( 2 )
#define comSTACK_SIZE ( ( unsigned short ) 256 )
#define comRX_RELATIVE_PRIORITY ( 1 )
/* Handle to the com port used by both tasks. */
static xComPortHandle xPort;
/* The transmit function as described at the top of the file. */
static void vComTxTask( void *pvParameters );
/* The receive function as described at the top of the file. */
static void vComRxTask( void *pvParameters );
/* The semaphore test function as described at the top of the file. */
static void vSemTestTask( void * pvParameters );
/* The string that is repeatedly transmitted. */
const char * const pcMessageToExchange = "Send this message over and over again to check communications interrupts. "
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n";
/* Variables that are incremented on each cycle of each task. These are used to
check that both tasks are still executing. */
volatile short sTxCount = 0, sRxCount = 0, sSemCount = 0;
/* The handle to the semaphore test task. */
static TaskHandle_t xSemTestTaskHandle = NULL;
/*-----------------------------------------------------------*/
void vStartComTestTasks( unsigned portBASE_TYPE uxPriority, eCOMPort ePort, eBaud eBaudRate )
{
const unsigned portBASE_TYPE uxBufferLength = 255;
/* Initialise the com port then spawn both tasks. */
xPort = xSerialPortInit( ePort, eBaudRate, serNO_PARITY, serBITS_8, serSTOP_1, uxBufferLength );
xTaskCreate( vComTxTask, "COMTx", comSTACK_SIZE, NULL, uxPriority, NULL );
xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority + comRX_RELATIVE_PRIORITY, NULL );
xTaskCreate( vSemTestTask, "ISRSem", comSTACK_SIZE, NULL, tskIDLE_PRIORITY, &xSemTestTaskHandle );
}
/*-----------------------------------------------------------*/
static void vComTxTask( void *pvParameters )
{
const char * const pcTaskStartMsg = "COM Tx task started.\r\n";
TickType_t xTimeToWait;
/* Stop warnings. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
/* Send the string to the serial port. */
vSerialPutString( xPort, pcMessageToExchange, strlen( pcMessageToExchange ) );
/* We have posted all the characters in the string - increment the variable
used to check that this task is still running, then wait before re-sending
the string. */
sTxCount++;
xTimeToWait = xTaskGetTickCount();
/* Make sure we don't wait too long... */
xTimeToWait %= comTX_MAX_BLOCK_TIME;
/* ...but we do want to wait. */
if( xTimeToWait < comTX_MIN_BLOCK_TIME )
{
xTimeToWait = comTX_MIN_BLOCK_TIME;
}
vTaskDelay( xTimeToWait );
}
} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */
/*-----------------------------------------------------------*/
static void vComRxTask( void *pvParameters )
{
const char * const pcTaskStartMsg = "COM Rx task started.\r\n";
const char * const pcTaskErrorMsg = "COM read error\r\n";
const char * const pcTaskRestartMsg = "COM resynced\r\n";
const char * const pcTaskTimeoutMsg = "COM Rx timed out\r\n";
const TickType_t xBlockTime = ( TickType_t ) 0xffff / portTICK_PERIOD_MS;
const char *pcExpectedChar;
portBASE_TYPE xGotChar;
char cRxedChar;
short sResyncRequired, sConsecutiveErrors, sLatchedError;
/* Stop warnings. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The first expected character is the first character in the string. */
pcExpectedChar = pcMessageToExchange;
sResyncRequired = pdFALSE;
sConsecutiveErrors = 0;
sLatchedError = pdFALSE;
for( ;; )
{
/* Receive a message from the com port interrupt routine. If a message is
not yet available the call will block the task. */
xGotChar = xSerialGetChar( xPort, &cRxedChar, xBlockTime );
if( xGotChar == pdTRUE )
{
if( sResyncRequired == pdTRUE )
{
/* We got out of sequence and are waiting for the start of the next
transmission of the string. */
if( cRxedChar == '\n' )
{
/* This is the end of the message so we can start again - with
the first character in the string being the next thing we expect
to receive. */
pcExpectedChar = pcMessageToExchange;
sResyncRequired = pdFALSE;
/* Queue a message for printing to say that we are going to try
again. */
vPrintDisplayMessage( &pcTaskRestartMsg );
/* Stop incrementing the check variable, if consecutive errors occur. */
sConsecutiveErrors++;
if( sConsecutiveErrors >= comMAX_CONSECUTIVE_ERRORS )
{
sLatchedError = pdTRUE;
}
}
}
else
{
/* We have received a character, but is it the expected character? */
if( cRxedChar != *pcExpectedChar )
{
/* This was not the expected character so post a message for
printing to say that an error has occurred. We will then wait
to resynchronise. */
vPrintDisplayMessage( &pcTaskErrorMsg );
sResyncRequired = pdTRUE;
}
else
{
/* This was the expected character so next time we will expect
the next character in the string. Wrap back to the beginning
of the string when the null terminator has been reached. */
pcExpectedChar++;
if( *pcExpectedChar == '\0' )
{
pcExpectedChar = pcMessageToExchange;
/* We have got through the entire string without error. */
sConsecutiveErrors = 0;
}
}
}
/* Increment the count that is used to check that this task is still
running. This is only done if an error has never occurred. */
if( sLatchedError == pdFALSE )
{
sRxCount++;
}
}
else
{
vPrintDisplayMessage( &pcTaskTimeoutMsg );
}
}
} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */
/*-----------------------------------------------------------*/
static void vSemTestTask( void * pvParameters )
{
const char * const pcTaskStartMsg = "ISR Semaphore test started.\r\n";
portBASE_TYPE xError = pdFALSE;
/* Stop warnings. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
if( xSerialWaitForSemaphore( xPort ) )
{
if( xError == pdFALSE )
{
sSemCount++;
}
}
else
{
xError = pdTRUE;
}
}
} /*lint !e715 !e830 !e818 pvParameters not used but function prototype must be standard for task function. */
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
portBASE_TYPE xAreComTestTasksStillRunning( void )
{
static short sLastTxCount = 0, sLastRxCount = 0, sLastSemCount = 0;
portBASE_TYPE xReturn;
/* Not too worried about mutual exclusion on these variables as they are 16
bits and we are only reading them. We also only care to see if they have
changed or not. */
if( ( sTxCount == sLastTxCount ) || ( sRxCount == sLastRxCount ) || ( sSemCount == sLastSemCount ) )
{
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
sLastTxCount = sTxCount;
sLastRxCount = sRxCount;
sLastSemCount = sSemCount;
return xReturn;
}
/*-----------------------------------------------------------*/
void vComTestUnsuspendTask( void )
{
/* The task that is suspended on the semaphore will be referenced from the
Suspended list as it is blocking indefinitely. This call just checks that
the kernel correctly detects this and does not attempt to unsuspend the
task. */
xTaskResumeFromISR( xSemTestTaskHandle );
}

View File

@ -0,0 +1,203 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Create a single persistent task which periodically dynamically creates another
* four tasks. The original task is called the creator task, the four tasks it
* creates are called suicidal tasks.
*
* Two of the created suicidal tasks kill one other suicidal task before killing
* themselves - leaving just the original task remaining.
*
* The creator task must be spawned after all of the other demo application tasks
* as it keeps a check on the number of tasks under the scheduler control. The
* number of tasks it expects to see running should never be greater than the
* number of tasks that were in existence when the creator task was spawned, plus
* one set of four suicidal tasks. If this number is exceeded an error is flagged.
*
* \page DeathC death.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "death.h"
#include "print.h"
#define deathSTACK_SIZE ( ( unsigned short ) 512 )
/* The task originally created which is responsible for periodically dynamically
creating another four tasks. */
static void vCreateTasks( void *pvParameters );
/* The task function of the dynamically created tasks. */
static void vSuicidalTask( void *pvParameters );
/* A variable which is incremented every time the dynamic tasks are created. This
is used to check that the task is still running. */
static volatile short sCreationCount = 0;
/* Used to store the number of tasks that were originally running so the creator
task can tell if any of the suicidal tasks have failed to die. */
static volatile unsigned portBASE_TYPE uxTasksRunningAtStart = 0;
static const unsigned portBASE_TYPE uxMaxNumberOfExtraTasksRunning = 5;
/* Used to store a handle to the tasks that should be killed by a suicidal task,
before it kills itself. */
TaskHandle_t xCreatedTask1, xCreatedTask2;
/*-----------------------------------------------------------*/
void vCreateSuicidalTasks( unsigned portBASE_TYPE uxPriority )
{
unsigned portBASE_TYPE *puxPriority;
/* Create the Creator tasks - passing in as a parameter the priority at which
the suicidal tasks should be created. */
puxPriority = ( unsigned portBASE_TYPE * ) pvPortMalloc( sizeof( unsigned portBASE_TYPE ) );
*puxPriority = uxPriority;
xTaskCreate( vCreateTasks, "CREATOR", deathSTACK_SIZE, ( void * ) puxPriority, uxPriority, NULL );
/* Record the number of tasks that are running now so we know if any of the
suicidal tasks have failed to be killed. */
uxTasksRunningAtStart = uxTaskGetNumberOfTasks();
}
/*-----------------------------------------------------------*/
static void vSuicidalTask( void *pvParameters )
{
portDOUBLE d1, d2;
TaskHandle_t xTaskToKill;
const TickType_t xDelay = ( TickType_t ) 500 / portTICK_PERIOD_MS;
if( pvParameters != NULL )
{
/* This task is periodically created four times. Tow created tasks are
passed a handle to the other task so it can kill it before killing itself.
The other task is passed in null. */
xTaskToKill = *( TaskHandle_t* )pvParameters;
}
else
{
xTaskToKill = NULL;
}
for( ;; )
{
/* Do something random just to use some stack and registers. */
d1 = 2.4;
d2 = 89.2;
d2 *= d1;
vTaskDelay( xDelay );
if( xTaskToKill != NULL )
{
/* Make sure the other task has a go before we delete it. */
vTaskDelay( ( TickType_t ) 0 );
/* Kill the other task that was created by vCreateTasks(). */
vTaskDelete( xTaskToKill );
/* Kill ourselves. */
vTaskDelete( NULL );
}
}
}/*lint !e818 !e550 Function prototype must be as per standard for task functions. */
/*-----------------------------------------------------------*/
static void vCreateTasks( void *pvParameters )
{
const TickType_t xDelay = ( TickType_t ) 1000 / portTICK_PERIOD_MS;
unsigned portBASE_TYPE uxPriority;
const char * const pcTaskStartMsg = "Create task started.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
uxPriority = *( unsigned portBASE_TYPE * ) pvParameters;
vPortFree( pvParameters );
for( ;; )
{
/* Just loop round, delaying then creating the four suicidal tasks. */
vTaskDelay( xDelay );
xTaskCreate( vSuicidalTask, "SUICIDE1", deathSTACK_SIZE, NULL, uxPriority, &xCreatedTask1 );
xTaskCreate( vSuicidalTask, "SUICIDE2", deathSTACK_SIZE, &xCreatedTask1, uxPriority, NULL );
xTaskCreate( vSuicidalTask, "SUICIDE1", deathSTACK_SIZE, NULL, uxPriority, &xCreatedTask2 );
xTaskCreate( vSuicidalTask, "SUICIDE2", deathSTACK_SIZE, &xCreatedTask2, uxPriority, NULL );
++sCreationCount;
}
}
/*-----------------------------------------------------------*/
/* This is called to check that the creator task is still running and that there
are not any more than four extra tasks. */
portBASE_TYPE xIsCreateTaskStillRunning( void )
{
static short sLastCreationCount = 0;
short sReturn = pdTRUE;
unsigned portBASE_TYPE uxTasksRunningNow;
if( sLastCreationCount == sCreationCount )
{
sReturn = pdFALSE;
}
uxTasksRunningNow = uxTaskGetNumberOfTasks();
if( uxTasksRunningNow < uxTasksRunningAtStart )
{
sReturn = pdFALSE;
}
else if( ( uxTasksRunningNow - uxTasksRunningAtStart ) > uxMaxNumberOfExtraTasksRunning )
{
sReturn = pdFALSE;
}
else
{
/* Everything is okay. */
}
return sReturn;
}

View File

@ -0,0 +1,578 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* The first test creates three tasks - two counter tasks (one continuous count
* and one limited count) and one controller. A "count" variable is shared
* between all three tasks. The two counter tasks should never be in a "ready"
* state at the same time. The controller task runs at the same priority as
* the continuous count task, and at a lower priority than the limited count
* task.
*
* One counter task loops indefinitely, incrementing the shared count variable
* on each iteration. To ensure it has exclusive access to the variable it
* raises it's priority above that of the controller task before each
* increment, lowering it again to it's original priority before starting the
* next iteration.
*
* The other counter task increments the shared count variable on each
* iteration of it's loop until the count has reached a limit of 0xff - at
* which point it suspends itself. It will not start a new loop until the
* controller task has made it "ready" again by calling vTaskResume ().
* This second counter task operates at a higher priority than controller
* task so does not need to worry about mutual exclusion of the counter
* variable.
*
* The controller task is in two sections. The first section controls and
* monitors the continuous count task. When this section is operational the
* limited count task is suspended. Likewise, the second section controls
* and monitors the limited count task. When this section is operational the
* continuous count task is suspended.
*
* In the first section the controller task first takes a copy of the shared
* count variable. To ensure mutual exclusion on the count variable it
* suspends the continuous count task, resuming it again when the copy has been
* taken. The controller task then sleeps for a fixed period - during which
* the continuous count task will execute and increment the shared variable.
* When the controller task wakes it checks that the continuous count task
* has executed by comparing the copy of the shared variable with its current
* value. This time, to ensure mutual exclusion, the scheduler itself is
* suspended with a call to vTaskSuspendAll (). This is for demonstration
* purposes only and is not a recommended technique due to its inefficiency.
*
* After a fixed number of iterations the controller task suspends the
* continuous count task, and moves on to its second section.
*
* At the start of the second section the shared variable is cleared to zero.
* The limited count task is then woken from it's suspension by a call to
* vTaskResume (). As this counter task operates at a higher priority than
* the controller task the controller task should not run again until the
* shared variable has been counted up to the limited value causing the counter
* task to suspend itself. The next line after vTaskResume () is therefore
* a check on the shared variable to ensure everything is as expected.
*
*
* The second test consists of a couple of very simple tasks that post onto a
* queue while the scheduler is suspended. This test was added to test parts
* of the scheduler not exercised by the first test.
*
*
* The final set of two tasks implements a third test. This simply raises the
* priority of a task while the scheduler is suspended. Again this test was
* added to exercise parts of the code not covered by the first test.
*
* \page Priorities dynamic.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
+ Added a second, simple test that uses the functions
vQueueReceiveWhenSuspendedTask() and vQueueSendWhenSuspendedTask().
Changes from V3.1.1
+ Added a third simple test that uses the vTaskPrioritySet() function
while the scheduler is suspended.
+ Modified the controller task slightly to test the calling of
vTaskResumeAll() while the scheduler is suspended.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo app include files. */
#include "dynamic.h"
#include "print.h"
/* Function that implements the "limited count" task as described above. */
static void vLimitedIncrementTask( void * pvParameters );
/* Function that implements the "continuous count" task as described above. */
static void vContinuousIncrementTask( void * pvParameters );
/* Function that implements the controller task as described above. */
static void vCounterControlTask( void * pvParameters );
/* The simple test functions that check sending and receiving while the
scheduler is suspended. */
static void vQueueReceiveWhenSuspendedTask( void *pvParameters );
static void vQueueSendWhenSuspendedTask( void *pvParameters );
/* The simple test functions that check raising and lowering of task priorities
while the scheduler is suspended. */
static void prvChangePriorityWhenSuspendedTask( void *pvParameters );
static void prvChangePriorityHelperTask( void *pvParameters );
/* Demo task specific constants. */
#define priSTACK_SIZE ( ( unsigned short ) configMINIMAL_STACK_SIZE )
#define priSLEEP_TIME ( ( TickType_t ) 50 )
#define priLOOPS ( 5 )
#define priMAX_COUNT ( ( unsigned long ) 0xff )
#define priNO_BLOCK ( ( TickType_t ) 0 )
#define priSUSPENDED_QUEUE_LENGTH ( 1 )
/*-----------------------------------------------------------*/
/* Handles to the two counter tasks. These could be passed in as parameters
to the controller task to prevent them having to be file scope. */
static TaskHandle_t xContinuousIncrementHandle, xLimitedIncrementHandle, xChangePriorityWhenSuspendedHandle;
/* The shared counter variable. This is passed in as a parameter to the two
counter variables for demonstration purposes. */
static unsigned long ulCounter;
/* Variable used in a similar way by the test that checks the raising and
lowering of task priorities while the scheduler is suspended. */
static unsigned long ulPrioritySetCounter;
/* Variables used to check that the tasks are still operating without error.
Each complete iteration of the controller task increments this variable
provided no errors have been found. The variable maintaining the same value
is therefore indication of an error. */
static unsigned short usCheckVariable = ( unsigned short ) 0;
static portBASE_TYPE xSuspendedQueueSendError = pdFALSE;
static portBASE_TYPE xSuspendedQueueReceiveError = pdFALSE;
static portBASE_TYPE xPriorityRaiseWhenSuspendedError = pdFALSE;
/* Queue used by the second test. */
QueueHandle_t xSuspendedTestQueue;
/*-----------------------------------------------------------*/
/*
* Start the seven tasks as described at the top of the file.
* Note that the limited count task is given a higher priority.
*/
void vStartDynamicPriorityTasks( void )
{
xSuspendedTestQueue = xQueueCreate( priSUSPENDED_QUEUE_LENGTH, sizeof( unsigned long ) );
xTaskCreate( vContinuousIncrementTask, "CONT_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY, &xContinuousIncrementHandle );
xTaskCreate( vLimitedIncrementTask, "LIM_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY + 1, &xLimitedIncrementHandle );
xTaskCreate( vCounterControlTask, "C_CTRL", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
xTaskCreate( vQueueSendWhenSuspendedTask, "SUSP_SEND", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
xTaskCreate( vQueueReceiveWhenSuspendedTask, "SUSP_RECV", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
xTaskCreate( prvChangePriorityWhenSuspendedTask, "1st_P_CHANGE", priSTACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
xTaskCreate( prvChangePriorityHelperTask, "2nd_P_CHANGE", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, &xChangePriorityWhenSuspendedHandle );
}
/*-----------------------------------------------------------*/
/*
* Just loops around incrementing the shared variable until the limit has been
* reached. Once the limit has been reached it suspends itself.
*/
static void vLimitedIncrementTask( void * pvParameters )
{
unsigned long *pulCounter;
/* Take a pointer to the shared variable from the parameters passed into
the task. */
pulCounter = ( unsigned long * ) pvParameters;
/* This will run before the control task, so the first thing it does is
suspend - the control task will resume it when ready. */
vTaskSuspend( NULL );
for( ;; )
{
/* Just count up to a value then suspend. */
( *pulCounter )++;
if( *pulCounter >= priMAX_COUNT )
{
vTaskSuspend( NULL );
}
}
}
/*-----------------------------------------------------------*/
/*
* Just keep counting the shared variable up. The control task will suspend
* this task when it wants.
*/
static void vContinuousIncrementTask( void * pvParameters )
{
unsigned long *pulCounter;
unsigned portBASE_TYPE uxOurPriority;
/* Take a pointer to the shared variable from the parameters passed into
the task. */
pulCounter = ( unsigned long * ) pvParameters;
/* Query our priority so we can raise it when exclusive access to the
shared variable is required. */
uxOurPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* Raise our priority above the controller task to ensure a context
switch does not occur while we are accessing this variable. */
vTaskPrioritySet( NULL, uxOurPriority + 1 );
( *pulCounter )++;
vTaskPrioritySet( NULL, uxOurPriority );
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
/*
* Controller task as described above.
*/
static void vCounterControlTask( void * pvParameters )
{
unsigned long ulLastCounter;
short sLoops;
short sError = pdFALSE;
const char * const pcTaskStartMsg = "Priority manipulation tasks started.\r\n";
const char * const pcTaskFailMsg = "Priority manipulation Task Failed\r\n";
/* Just to stop warning messages. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
/* Start with the counter at zero. */
ulCounter = ( unsigned long ) 0;
/* First section : */
/* Check the continuous count task is running. */
for( sLoops = 0; sLoops < priLOOPS; sLoops++ )
{
/* Suspend the continuous count task so we can take a mirror of the
shared variable without risk of corruption. */
vTaskSuspend( xContinuousIncrementHandle );
ulLastCounter = ulCounter;
vTaskResume( xContinuousIncrementHandle );
/* Now delay to ensure the other task has processor time. */
vTaskDelay( priSLEEP_TIME );
/* Check the shared variable again. This time to ensure mutual
exclusion the whole scheduler will be locked. This is just for
demo purposes! */
vTaskSuspendAll();
{
if( ulLastCounter == ulCounter )
{
/* The shared variable has not changed. There is a problem
with the continuous count task so flag an error. */
sError = pdTRUE;
xTaskResumeAll();
vPrintDisplayMessage( &pcTaskFailMsg );
vTaskSuspendAll();
}
}
xTaskResumeAll();
}
/* Second section: */
/* Suspend the continuous counter task so it stops accessing the shared variable. */
vTaskSuspend( xContinuousIncrementHandle );
/* Reset the variable. */
ulCounter = ( unsigned long ) 0;
/* Resume the limited count task which has a higher priority than us.
We should therefore not return from this call until the limited count
task has suspended itself with a known value in the counter variable.
The scheduler suspension is not necessary but is included for test
purposes. */
vTaskSuspendAll();
vTaskResume( xLimitedIncrementHandle );
xTaskResumeAll();
/* Does the counter variable have the expected value? */
if( ulCounter != priMAX_COUNT )
{
sError = pdTRUE;
vPrintDisplayMessage( &pcTaskFailMsg );
}
if( sError == pdFALSE )
{
/* If no errors have occurred then increment the check variable. */
portENTER_CRITICAL();
usCheckVariable++;
portEXIT_CRITICAL();
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Resume the continuous count task and do it all again. */
vTaskResume( xContinuousIncrementHandle );
}
}
/*-----------------------------------------------------------*/
static void vQueueSendWhenSuspendedTask( void *pvParameters )
{
static unsigned long ulValueToSend = ( unsigned long ) 0;
const char * const pcTaskStartMsg = "Queue send while suspended task started.\r\n";
const char * const pcTaskFailMsg = "Queue send while suspended failed.\r\n";
/* Just to stop warning messages. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
vTaskSuspendAll();
{
/* We must not block while the scheduler is suspended! */
if( xQueueSend( xSuspendedTestQueue, ( void * ) &ulValueToSend, priNO_BLOCK ) != pdTRUE )
{
if( xSuspendedQueueSendError == pdFALSE )
{
xTaskResumeAll();
vPrintDisplayMessage( &pcTaskFailMsg );
vTaskSuspendAll();
}
xSuspendedQueueSendError = pdTRUE;
}
}
xTaskResumeAll();
vTaskDelay( priSLEEP_TIME );
++ulValueToSend;
}
}
/*-----------------------------------------------------------*/
static void vQueueReceiveWhenSuspendedTask( void *pvParameters )
{
static unsigned long ulExpectedValue = ( unsigned long ) 0, ulReceivedValue;
const char * const pcTaskStartMsg = "Queue receive while suspended task started.\r\n";
const char * const pcTaskFailMsg = "Queue receive while suspended failed.\r\n";
portBASE_TYPE xGotValue;
/* Just to stop warning messages. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
do
{
/* Suspending the scheduler here is fairly pointless and
undesirable for a normal application. It is done here purely
to test the scheduler. The inner xTaskResumeAll() should
never return pdTRUE as the scheduler is still locked by the
outer call. */
vTaskSuspendAll();
{
vTaskSuspendAll();
{
xGotValue = xQueueReceive( xSuspendedTestQueue, ( void * ) &ulReceivedValue, priNO_BLOCK );
}
if( xTaskResumeAll() )
{
xSuspendedQueueReceiveError = pdTRUE;
}
}
xTaskResumeAll();
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
} while( xGotValue == pdFALSE );
if( ulReceivedValue != ulExpectedValue )
{
if( xSuspendedQueueReceiveError == pdFALSE )
{
vPrintDisplayMessage( &pcTaskFailMsg );
}
xSuspendedQueueReceiveError = pdTRUE;
}
++ulExpectedValue;
}
}
/*-----------------------------------------------------------*/
static void prvChangePriorityWhenSuspendedTask( void *pvParameters )
{
const char * const pcTaskStartMsg = "Priority change when suspended task started.\r\n";
const char * const pcTaskFailMsg = "Priority change when suspended task failed.\r\n";
/* Just to stop warning messages. */
( void ) pvParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
/* Start with the counter at 0 so we know what the counter should be
when we check it next. */
ulPrioritySetCounter = ( unsigned long ) 0;
/* Resume the helper task. At this time it has a priority lower than
ours so no context switch should occur. */
vTaskResume( xChangePriorityWhenSuspendedHandle );
/* Check to ensure the task just resumed has not executed. */
portENTER_CRITICAL();
{
if( ulPrioritySetCounter != ( unsigned long ) 0 )
{
xPriorityRaiseWhenSuspendedError = pdTRUE;
vPrintDisplayMessage( &pcTaskFailMsg );
}
}
portEXIT_CRITICAL();
/* Now try raising the priority while the scheduler is suspended. */
vTaskSuspendAll();
{
vTaskPrioritySet( xChangePriorityWhenSuspendedHandle, ( configMAX_PRIORITIES - 1 ) );
/* Again, even though the helper task has a priority greater than
ours, it should not have executed yet because the scheduler is
suspended. */
portENTER_CRITICAL();
{
if( ulPrioritySetCounter != ( unsigned long ) 0 )
{
xPriorityRaiseWhenSuspendedError = pdTRUE;
vPrintDisplayMessage( &pcTaskFailMsg );
}
}
portEXIT_CRITICAL();
}
xTaskResumeAll();
/* Now the scheduler has been resumed the helper task should
immediately preempt us and execute. When it executes it will increment
the ulPrioritySetCounter exactly once before suspending itself.
We should now always find the counter set to 1. */
portENTER_CRITICAL();
{
if( ulPrioritySetCounter != ( unsigned long ) 1 )
{
xPriorityRaiseWhenSuspendedError = pdTRUE;
vPrintDisplayMessage( &pcTaskFailMsg );
}
}
portEXIT_CRITICAL();
/* Delay until we try this again. */
vTaskDelay( priSLEEP_TIME * 2 );
/* Set the priority of the helper task back ready for the next
execution of this task. */
vTaskSuspendAll();
vTaskPrioritySet( xChangePriorityWhenSuspendedHandle, tskIDLE_PRIORITY );
xTaskResumeAll();
}
}
/*-----------------------------------------------------------*/
static void prvChangePriorityHelperTask( void *pvParameters )
{
/* Just to stop warning messages. */
( void ) pvParameters;
for( ;; )
{
/* This is the helper task for prvChangePriorityWhenSuspendedTask().
It has it's priority raised and lowered. When it runs it simply
increments the counter then suspends itself again. This allows
prvChangePriorityWhenSuspendedTask() to know how many times it has
executed. */
ulPrioritySetCounter++;
vTaskSuspend( NULL );
}
}
/*-----------------------------------------------------------*/
/* Called to check that all the created tasks are still running without error. */
portBASE_TYPE xAreDynamicPriorityTasksStillRunning( void )
{
/* Keep a history of the check variables so we know if it has been incremented
since the last call. */
static unsigned short usLastTaskCheck = ( unsigned short ) 0;
portBASE_TYPE xReturn = pdTRUE;
/* Check the tasks are still running by ensuring the check variable
is still incrementing. */
if( usCheckVariable == usLastTaskCheck )
{
/* The check has not incremented so an error exists. */
xReturn = pdFALSE;
}
if( xSuspendedQueueSendError == pdTRUE )
{
xReturn = pdFALSE;
}
if( xSuspendedQueueReceiveError == pdTRUE )
{
xReturn = pdFALSE;
}
if( xPriorityRaiseWhenSuspendedError == pdTRUE )
{
xReturn = pdFALSE;
}
usLastTaskCheck = usCheckVariable;
return xReturn;
}

View File

@ -0,0 +1,368 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* This file exercises the event mechanism whereby more than one task is
* blocked waiting for the same event.
*
* The demo creates five tasks - four 'event' tasks, and a controlling task.
* The event tasks have various different priorities and all block on reading
* the same queue. The controlling task writes data to the queue, then checks
* to see which of the event tasks read the data from the queue. The
* controlling task has the lowest priority of all the tasks so is guaranteed
* to always get preempted immediately upon writing to the queue.
*
* By selectively suspending and resuming the event tasks the controlling task
* can check that the highest priority task that is blocked on the queue is the
* task that reads the posted data from the queue.
*
* Two of the event tasks share the same priority. When neither of these tasks
* are suspended they should alternate - one reading one message from the queue,
* the other the next message, etc.
*/
/* Standard includes. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo program include files. */
#include "mevents.h"
#include "print.h"
/* Demo specific constants. */
#define evtSTACK_SIZE ( ( unsigned portBASE_TYPE ) configMINIMAL_STACK_SIZE )
#define evtNUM_TASKS ( 4 )
#define evtQUEUE_LENGTH ( ( unsigned portBASE_TYPE ) 3 )
#define evtNO_DELAY 0
/* Just indexes used to uniquely identify the tasks. Note that two tasks are
'highest' priority. */
#define evtHIGHEST_PRIORITY_INDEX_2 3
#define evtHIGHEST_PRIORITY_INDEX_1 2
#define evtMEDIUM_PRIORITY_INDEX 1
#define evtLOWEST_PRIORITY_INDEX 0
/* Each event task increments one of these counters each time it reads data
from the queue. */
static volatile portBASE_TYPE xTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };
/* Each time the controlling task posts onto the queue it increments the
expected count of the task that it expected to read the data from the queue
(i.e. the task with the highest priority that should be blocked on the queue).
xExpectedTaskCounters are incremented from the controlling task, and
xTaskCounters are incremented from the individual event tasks - therefore
comparing xTaskCounters to xExpectedTaskCounters shows whether or not the
correct task was unblocked by the post. */
static portBASE_TYPE xExpectedTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };
/* Handles to the four event tasks. These are required to suspend and resume
the tasks. */
static TaskHandle_t xCreatedTasks[ evtNUM_TASKS ];
/* The single queue onto which the controlling task posts, and the four event
tasks block. */
static QueueHandle_t xQueue;
/* Flag used to indicate whether or not an error has occurred at any time.
An error is either the queue being full when not expected, or an unexpected
task reading data from the queue. */
static portBASE_TYPE xHealthStatus = pdPASS;
/*-----------------------------------------------------------*/
/* Function that implements the event task. This is created four times. */
static void prvMultiEventTask( void *pvParameters );
/* Function that implements the controlling task. */
static void prvEventControllerTask( void *pvParameters );
/* This is a utility function that posts data to the queue, then compares
xExpectedTaskCounters with xTaskCounters to ensure everything worked as
expected.
The event tasks all have higher priorities the controlling task. Therefore
the controlling task will always get preempted between writhing to the queue
and checking the task counters.
@param xExpectedTask The index to the task that the controlling task thinks
should be the highest priority task waiting for data, and
therefore the task that will unblock.
@param xIncrement The number of items that should be written to the queue.
*/
static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement );
/* This is just incremented each cycle of the controlling tasks function so
the main application can ensure the test is still running. */
static portBASE_TYPE xCheckVariable = 0;
/*-----------------------------------------------------------*/
void vStartMultiEventTasks( void )
{
/* Create the queue to be used for all the communications. */
xQueue = xQueueCreate( evtQUEUE_LENGTH, ( unsigned portBASE_TYPE ) sizeof( unsigned portBASE_TYPE ) );
/* Start the controlling task. This has the idle priority to ensure it is
always preempted by the event tasks. */
xTaskCreate( prvEventControllerTask, "EvntCTRL", evtSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
/* Start the four event tasks. Note that two have priority 3, one
priority 2 and the other priority 1. */
xTaskCreate( prvMultiEventTask, "Event0", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 0 ] ), 1, &( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ) );
xTaskCreate( prvMultiEventTask, "Event1", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 1 ] ), 2, &( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ) );
xTaskCreate( prvMultiEventTask, "Event2", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 2 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ) );
xTaskCreate( prvMultiEventTask, "Event3", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 3 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ) );
}
/*-----------------------------------------------------------*/
static void prvMultiEventTask( void *pvParameters )
{
portBASE_TYPE *pxCounter;
unsigned portBASE_TYPE uxDummy;
const char * const pcTaskStartMsg = "Multi event task started.\r\n";
/* The variable this task will increment is passed in as a parameter. */
pxCounter = ( portBASE_TYPE * ) pvParameters;
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
/* Block on the queue. */
if( xQueueReceive( xQueue, &uxDummy, portMAX_DELAY ) )
{
/* We unblocked by reading the queue - so simply increment
the counter specific to this task instance. */
( *pxCounter )++;
}
else
{
xHealthStatus = pdFAIL;
}
}
}
/*-----------------------------------------------------------*/
static void prvEventControllerTask( void *pvParameters )
{
const char * const pcTaskStartMsg = "Multi event controller task started.\r\n";
portBASE_TYPE xDummy = 0;
/* Just to stop warnings. */
( void ) pvParameters;
vPrintDisplayMessage( &pcTaskStartMsg );
for( ;; )
{
/* All tasks are blocked on the queue. When a message is posted one of
the two tasks that share the highest priority should unblock to read
the queue. The next message written should unblock the other task with
the same high priority, and so on in order. No other task should
unblock to read data as they have lower priorities. */
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
/* For the rest of these tests we don't need the second 'highest'
priority task - so it is suspended. */
vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );
/* Now suspend the other highest priority task. The medium priority
task will then be the task with the highest priority that remains
blocked on the queue. */
vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
/* This time, when we post onto the queue we will expect the medium
priority task to unblock and preempt us. */
prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );
/* Now try resuming the highest priority task while the scheduler is
suspended. The task should start executing as soon as the scheduler
is resumed - therefore when we post to the queue again, the highest
priority task should again preempt us. */
vTaskSuspendAll();
vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
xTaskResumeAll();
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
/* Now we are going to suspend the high and medium priority tasks. The
low priority task should then preempt us. Again the task suspension is
done with the whole scheduler suspended just for test purposes. */
vTaskSuspendAll();
vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
xTaskResumeAll();
prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );
/* Do the same basic test another few times - selectively suspending
and resuming tasks and each time calling prvCheckTaskCounters() passing
to the function the number of the task we expected to be unblocked by
the post. */
vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
vTaskSuspendAll(); /* Just for test. */
vTaskSuspendAll(); /* Just for test. */
vTaskSuspendAll(); /* Just for even more test. */
vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
xTaskResumeAll();
xTaskResumeAll();
xTaskResumeAll();
prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );
vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );
vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );
/* Now a slight change, first suspend all tasks. */
vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
/* Now when we resume the low priority task and write to the queue 3
times. We expect the low priority task to service the queue three
times. */
vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, evtQUEUE_LENGTH );
/* Again suspend all tasks (only the low priority task is not suspended
already). */
vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
/* This time we are going to suspend the scheduler, resume the low
priority task, then resume the high priority task. In this state we
will write to the queue three times. When the scheduler is resumed
we expect the high priority task to service all three messages. */
vTaskSuspendAll();
{
vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );
vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );
for( xDummy = 0; xDummy < evtQUEUE_LENGTH; xDummy++ )
{
if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )
{
xHealthStatus = pdFAIL;
}
}
/* The queue should not have been serviced yet!. The scheduler
is still suspended. */
if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )
{
xHealthStatus = pdFAIL;
}
}
xTaskResumeAll();
/* We should have been preempted by resuming the scheduler - so by the
time we are running again we expect the high priority task to have
removed three items from the queue. */
xExpectedTaskCounters[ evtHIGHEST_PRIORITY_INDEX_1 ] += evtQUEUE_LENGTH;
if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )
{
xHealthStatus = pdFAIL;
}
/* The medium priority and second high priority tasks are still
suspended. Make sure to resume them before starting again. */
vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );
vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );
/* Just keep incrementing to show the task is still executing. */
xCheckVariable++;
}
}
/*-----------------------------------------------------------*/
static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement )
{
portBASE_TYPE xDummy = 0;
/* Write to the queue the requested number of times. The data written is
not important. */
for( xDummy = 0; xDummy < xIncrement; xDummy++ )
{
if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )
{
/* Did not expect to ever find the queue full. */
xHealthStatus = pdFAIL;
}
}
/* All the tasks blocked on the queue have a priority higher than the
controlling task. Writing to the queue will therefore have caused this
task to be preempted. By the time this line executes the event task will
have executed and incremented its counter. Increment the expected counter
to the same value. */
( xExpectedTaskCounters[ xExpectedTask ] ) += xIncrement;
/* Check the actual counts and expected counts really are the same. */
if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )
{
/* The counters were not the same. This means a task we did not expect
to unblock actually did unblock. */
xHealthStatus = pdFAIL;
}
}
/*-----------------------------------------------------------*/
portBASE_TYPE xAreMultiEventTasksStillRunning( void )
{
static portBASE_TYPE xPreviousCheckVariable = 0;
/* Called externally to periodically check that this test is still
operational. */
if( xPreviousCheckVariable == xCheckVariable )
{
xHealthStatus = pdFAIL;
}
xPreviousCheckVariable = xCheckVariable;
return xHealthStatus;
}

View File

@ -0,0 +1,128 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Creates eight tasks, each of which flash an LED at a different rate. The first
* LED flashes every 125ms, the second every 250ms, the third every 375ms, etc.
*
* The LED flash tasks provide instant visual feedback. They show that the scheduler
* is still operational.
*
* The PC port uses the standard parallel port for outputs, the Flashlite 186 port
* uses IO port F.
*
* \page flashC flash.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
Changes from V2.1.1
+ The stack size now uses configMINIMAL_STACK_SIZE.
+ String constants made file scope to decrease stack depth on 8051 port.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "partest.h"
#include "flash.h"
#include "print.h"
#define ledSTACK_SIZE configMINIMAL_STACK_SIZE
/* Structure used to pass parameters to the LED tasks. */
typedef struct LED_PARAMETERS
{
unsigned portBASE_TYPE uxLED; /*< The output the task should use. */
TickType_t xFlashRate; /*< The rate at which the LED should flash. */
} xLEDParameters;
/* The task that is created eight times - each time with a different xLEDParaemtes
structure passed in as the parameter. */
static void vLEDFlashTask( void *pvParameters );
/* String to print if USE_STDIO is defined. */
const char * const pcTaskStartMsg = "LED flash task started.\r\n";
/*-----------------------------------------------------------*/
void vStartLEDFlashTasks( unsigned portBASE_TYPE uxPriority )
{
unsigned portBASE_TYPE uxLEDTask;
xLEDParameters *pxLEDParameters;
const unsigned portBASE_TYPE uxNumOfLEDs = 8;
const TickType_t xFlashRate = 125;
/* Create the eight tasks. */
for( uxLEDTask = 0; uxLEDTask < uxNumOfLEDs; ++uxLEDTask )
{
/* Create and complete the structure used to pass parameters to the next
created task. */
pxLEDParameters = ( xLEDParameters * ) pvPortMalloc( sizeof( xLEDParameters ) );
pxLEDParameters->uxLED = uxLEDTask;
pxLEDParameters->xFlashRate = ( xFlashRate + ( xFlashRate * ( TickType_t ) uxLEDTask ) );
pxLEDParameters->xFlashRate /= portTICK_PERIOD_MS;
/* Spawn the task. */
xTaskCreate( vLEDFlashTask, "LEDx", ledSTACK_SIZE, ( void * ) pxLEDParameters, uxPriority, ( TaskHandle_t * ) NULL );
}
}
/*-----------------------------------------------------------*/
static void vLEDFlashTask( void *pvParameters )
{
xLEDParameters *pxParameters;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
pxParameters = ( xLEDParameters * ) pvParameters;
for(;;)
{
/* Delay for half the flash period then turn the LED on. */
vTaskDelay( pxParameters->xFlashRate / ( TickType_t ) 2 );
vParTestToggleLED( pxParameters->uxLED );
/* Delay for half the flash period then turn the LED off. */
vTaskDelay( pxParameters->xFlashRate / ( TickType_t ) 2 );
vParTestToggleLED( pxParameters->uxLED );
}
}

View File

@ -0,0 +1,331 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
Changes from V1.2.3
+ The created tasks now include calls to tskYIELD(), allowing them to be used
with the cooperative scheduler.
*/
/**
* Creates eight tasks, each of which loops continuously performing an (emulated)
* floating point calculation.
*
* All the tasks run at the idle priority and never block or yield. This causes
* all eight tasks to time slice with the idle task. Running at the idle priority
* means that these tasks will get pre-empted any time another task is ready to run
* or a time slice occurs. More often than not the pre-emption will occur mid
* calculation, creating a good test of the schedulers context switch mechanism - a
* calculation producing an unexpected result could be a symptom of a corruption in
* the context of a task.
*
* \page FlopC flop.c
* \ingroup DemoFiles
* <HR>
*/
#include <stdlib.h>
#include <math.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "print.h"
/* Demo program include files. */
#include "flop.h"
#define mathSTACK_SIZE ( ( unsigned short ) 512 )
#define mathNUMBER_OF_TASKS ( 8 )
/* Four tasks, each of which performs a different floating point calculation.
Each of the four is created twice. */
static void vCompetingMathTask1( void *pvParameters );
static void vCompetingMathTask2( void *pvParameters );
static void vCompetingMathTask3( void *pvParameters );
static void vCompetingMathTask4( void *pvParameters );
/* These variables are used to check that all the tasks are still running. If a
task gets a calculation wrong it will
stop incrementing its check variable. */
static volatile unsigned short usTaskCheck[ mathNUMBER_OF_TASKS ] = { ( unsigned short ) 0 };
/*-----------------------------------------------------------*/
void vStartMathTasks( unsigned portBASE_TYPE uxPriority )
{
xTaskCreate( vCompetingMathTask1, "Math1", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask2, "Math2", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask3, "Math3", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask4, "Math4", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask1, "Math5", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 4 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask2, "Math6", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 5 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask3, "Math7", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 6 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask4, "Math8", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 7 ] ), uxPriority, NULL );
}
/*-----------------------------------------------------------*/
static void vCompetingMathTask1( void *pvParameters )
{
portDOUBLE d1, d2, d3, d4;
volatile unsigned short *pusTaskCheckVariable;
const portDOUBLE dAnswer = ( 123.4567 + 2345.6789 ) * -918.222;
const char * const pcTaskStartMsg = "Math task 1 started.\r\n";
const char * const pcTaskFailMsg = "Math task 1 failed.\r\n";
short sError = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for(;;)
{
d1 = 123.4567;
d2 = 2345.6789;
d3 = -918.222;
d4 = ( d1 + d2 ) * d3;
taskYIELD();
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( fabs( d4 - dAnswer ) > 0.001 )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
taskYIELD();
}
}
/*-----------------------------------------------------------*/
static void vCompetingMathTask2( void *pvParameters )
{
portDOUBLE d1, d2, d3, d4;
volatile unsigned short *pusTaskCheckVariable;
const portDOUBLE dAnswer = ( -389.38 / 32498.2 ) * -2.0001;
const char * const pcTaskStartMsg = "Math task 2 started.\r\n";
const char * const pcTaskFailMsg = "Math task 2 failed.\r\n";
short sError = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for( ;; )
{
d1 = -389.38;
d2 = 32498.2;
d3 = -2.0001;
d4 = ( d1 / d2 ) * d3;
taskYIELD();
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( fabs( d4 - dAnswer ) > 0.001 )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know
this task is still running okay. */
( *pusTaskCheckVariable )++;
}
taskYIELD();
}
}
/*-----------------------------------------------------------*/
static void vCompetingMathTask3( void *pvParameters )
{
portDOUBLE *pdArray, dTotal1, dTotal2, dDifference;
volatile unsigned short *pusTaskCheckVariable;
const unsigned short usArraySize = 250;
unsigned short usPosition;
const char * const pcTaskStartMsg = "Math task 3 started.\r\n";
const char * const pcTaskFailMsg = "Math task 3 failed.\r\n";
short sError = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
pdArray = ( portDOUBLE * ) pvPortMalloc( ( size_t ) 250 * sizeof( portDOUBLE ) );
/* Keep filling an array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
dTotal1 = 0.0;
dTotal2 = 0.0;
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
pdArray[ usPosition ] = ( portDOUBLE ) usPosition + 5.5;
dTotal1 += ( portDOUBLE ) usPosition + 5.5;
}
taskYIELD();
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
dTotal2 += pdArray[ usPosition ];
}
dDifference = dTotal1 - dTotal2;
if( fabs( dDifference ) > 0.001 )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
taskYIELD();
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
static void vCompetingMathTask4( void *pvParameters )
{
portDOUBLE *pdArray, dTotal1, dTotal2, dDifference;
volatile unsigned short *pusTaskCheckVariable;
const unsigned short usArraySize = 250;
unsigned short usPosition;
const char * const pcTaskStartMsg = "Math task 4 started.\r\n";
const char * const pcTaskFailMsg = "Math task 4 failed.\r\n";
short sError = pdFALSE;
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
pdArray = ( portDOUBLE * ) pvPortMalloc( ( size_t ) 250 * sizeof( portDOUBLE ) );
/* Keep filling an array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
dTotal1 = 0.0;
dTotal2 = 0.0;
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
pdArray[ usPosition ] = ( portDOUBLE ) usPosition * 12.123;
dTotal1 += ( portDOUBLE ) usPosition * 12.123;
}
taskYIELD();
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
dTotal2 += pdArray[ usPosition ];
}
dDifference = dTotal1 - dTotal2;
if( fabs( dDifference ) > 0.001 )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
taskYIELD();
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
portBASE_TYPE xAreMathsTaskStillRunning( void )
{
/* Keep a history of the check variables so we know if they have been incremented
since the last call. */
static unsigned short usLastTaskCheck[ mathNUMBER_OF_TASKS ] = { ( unsigned short ) 0 };
portBASE_TYPE xReturn = pdTRUE, xTask;
/* Check the maths tasks are still running by ensuring their check variables
are still incrementing. */
for( xTask = 0; xTask < mathNUMBER_OF_TASKS; xTask++ )
{
if( usTaskCheck[ xTask ] == usLastTaskCheck[ xTask ] )
{
/* The check has not incremented so an error exists. */
xReturn = pdFALSE;
}
usLastTaskCheck[ xTask ] = usTaskCheck[ xTask ];
}
return xReturn;
}

View File

@ -0,0 +1,327 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
Changes from V1.2.3
+ The created tasks now include calls to tskYIELD(), allowing them to be used
with the cooperative scheduler.
*/
/**
* This does the same as flop. c, but uses variables of type long instead of
* type double.
*
* As with flop. c, the tasks created in this file are a good test of the
* scheduler context switch mechanism. The processor has to access 32bit
* variables in two or four chunks (depending on the processor). The low
* priority of these tasks means there is a high probability that a context
* switch will occur mid calculation. See the flop. c documentation for
* more information.
*
* \page IntegerC integer.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V1.2.1
+ The constants used in the calculations are larger to ensure the
optimiser does not truncate them to 16 bits.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "print.h"
/* Demo program include files. */
#include "integer.h"
#define intgSTACK_SIZE ( ( unsigned short ) 256 )
#define intgNUMBER_OF_TASKS ( 8 )
/* Four tasks, each of which performs a different calculation on four byte
variables. Each of the four is created twice. */
static void vCompeteingIntMathTask1( void *pvParameters );
static void vCompeteingIntMathTask2( void *pvParameters );
static void vCompeteingIntMathTask3( void *pvParameters );
static void vCompeteingIntMathTask4( void *pvParameters );
/* These variables are used to check that all the tasks are still running. If a
task gets a calculation wrong it will stop incrementing its check variable. */
static volatile unsigned short usTaskCheck[ intgNUMBER_OF_TASKS ] = { ( unsigned short ) 0 };
/*-----------------------------------------------------------*/
void vStartIntegerMathTasks( unsigned portBASE_TYPE uxPriority )
{
xTaskCreate( vCompeteingIntMathTask1, "IntMath1", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask2, "IntMath2", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask3, "IntMath3", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask4, "IntMath4", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask1, "IntMath5", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 4 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask2, "IntMath6", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 5 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask3, "IntMath7", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 6 ] ), uxPriority, NULL );
xTaskCreate( vCompeteingIntMathTask4, "IntMath8", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 7 ] ), uxPriority, NULL );
}
/*-----------------------------------------------------------*/
static void vCompeteingIntMathTask1( void *pvParameters )
{
long l1, l2, l3, l4;
short sError = pdFALSE;
volatile unsigned short *pusTaskCheckVariable;
const long lAnswer = ( ( long ) 74565L + ( long ) 1234567L ) * ( long ) -918L;
const char * const pcTaskStartMsg = "Integer math task 1 started.\r\n";
const char * const pcTaskFailMsg = "Integer math task 1 failed.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for(;;)
{
l1 = ( long ) 74565L;
l2 = ( long ) 1234567L;
l3 = ( long ) -918L;
l4 = ( l1 + l2 ) * l3;
taskYIELD();
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( l4 != lAnswer )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
static void vCompeteingIntMathTask2( void *pvParameters )
{
long l1, l2, l3, l4;
short sError = pdFALSE;
volatile unsigned short *pusTaskCheckVariable;
const long lAnswer = ( ( long ) -389000L / ( long ) 329999L ) * ( long ) -89L;
const char * const pcTaskStartMsg = "Integer math task 2 started.\r\n";
const char * const pcTaskFailMsg = "Integer math task 2 failed.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for( ;; )
{
l1 = -389000L;
l2 = 329999L;
l3 = -89L;
l4 = ( l1 / l2 ) * l3;
taskYIELD();
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( l4 != lAnswer )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
static void vCompeteingIntMathTask3( void *pvParameters )
{
long *plArray, lTotal1, lTotal2;
short sError = pdFALSE;
volatile unsigned short *pusTaskCheckVariable;
const unsigned short usArraySize = ( unsigned short ) 250;
unsigned short usPosition;
const char * const pcTaskStartMsg = "Integer math task 3 started.\r\n";
const char * const pcTaskFailMsg = "Integer math task 3 failed.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
/* Create the array we are going to use for our check calculation. */
plArray = ( long * ) pvPortMalloc( ( size_t ) 250 * sizeof( long ) );
/* Keep filling the array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
lTotal1 = ( long ) 0;
lTotal2 = ( long ) 0;
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
plArray[ usPosition ] = ( long ) usPosition + ( long ) 5;
lTotal1 += ( long ) usPosition + ( long ) 5;
}
taskYIELD();
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
lTotal2 += plArray[ usPosition ];
}
if( lTotal1 != lTotal2 )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
taskYIELD();
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
static void vCompeteingIntMathTask4( void *pvParameters )
{
long *plArray, lTotal1, lTotal2;
short sError = pdFALSE;
volatile unsigned short *pusTaskCheckVariable;
const unsigned short usArraySize = 250;
unsigned short usPosition;
const char * const pcTaskStartMsg = "Integer math task 4 started.\r\n";
const char * const pcTaskFailMsg = "Integer math task 4 failed.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( unsigned short * ) pvParameters;
/* Create the array we are going to use for our check calculation. */
plArray = ( long * ) pvPortMalloc( ( size_t ) 250 * sizeof( long ) );
/* Keep filling the array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
lTotal1 = ( long ) 0;
lTotal2 = ( long ) 0;
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
plArray[ usPosition ] = ( long ) usPosition * ( long ) 12;
lTotal1 += ( long ) usPosition * ( long ) 12;
}
taskYIELD();
for( usPosition = 0; usPosition < usArraySize; usPosition++ )
{
lTotal2 += plArray[ usPosition ];
}
if( lTotal1 != lTotal2 )
{
vPrintDisplayMessage( &pcTaskFailMsg );
sError = pdTRUE;
}
taskYIELD();
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
portBASE_TYPE xAreIntegerMathsTaskStillRunning( void )
{
/* Keep a history of the check variables so we know if they have been incremented
since the last call. */
static unsigned short usLastTaskCheck[ intgNUMBER_OF_TASKS ] = { ( unsigned short ) 0 };
portBASE_TYPE xReturn = pdTRUE, xTask;
/* Check the maths tasks are still running by ensuring their check variables
are still incrementing. */
for( xTask = 0; xTask < intgNUMBER_OF_TASKS; xTask++ )
{
if( usTaskCheck[ xTask ] == usLastTaskCheck[ xTask ] )
{
/* The check has not incremented so an error exists. */
xReturn = pdFALSE;
}
usLastTaskCheck[ xTask ] = usTaskCheck[ xTask ];
}
return xReturn;
}

View File

@ -0,0 +1,106 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Manages a queue of strings that are waiting to be displayed. This is used to
* ensure mutual exclusion of console output.
*
* A task wishing to display a message will call vPrintDisplayMessage (), with a
* pointer to the string as the parameter. The pointer is posted onto the
* xPrintQueue queue.
*
* The task spawned in main. c blocks on xPrintQueue. When a message becomes
* available it calls pcPrintGetNextMessage () to obtain a pointer to the next
* string, then uses the functions defined in the portable layer FileIO. c to
* display the message.
*
* <b>NOTE:</b>
* Using console IO can disrupt real time performance - depending on the port.
* Standard C IO routines are not designed for real time applications. While
* standard IO is useful for demonstration and debugging an alternative method
* should be used if you actually require console IO as part of your application.
*
* \page PrintC print.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "queue.h"
/* Demo program include files. */
#include "print.h"
static QueueHandle_t xPrintQueue;
/*-----------------------------------------------------------*/
void vPrintInitialise( void )
{
const unsigned portBASE_TYPE uxQueueSize = 20;
/* Create the queue on which errors will be reported. */
xPrintQueue = xQueueCreate( uxQueueSize, ( unsigned portBASE_TYPE ) sizeof( char * ) );
}
/*-----------------------------------------------------------*/
void vPrintDisplayMessage( const char * const * ppcMessageToSend )
{
#ifdef USE_STDIO
xQueueSend( xPrintQueue, ( void * ) ppcMessageToSend, ( TickType_t ) 0 );
#else
/* Stop warnings. */
( void ) ppcMessageToSend;
#endif
}
/*-----------------------------------------------------------*/
const char *pcPrintGetNextMessage( TickType_t xPrintRate )
{
char *pcMessage;
if( xQueueReceive( xPrintQueue, &pcMessage, xPrintRate ) == pdPASS )
{
return pcMessage;
}
else
{
return NULL;
}
}

View File

@ -0,0 +1,285 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Creates two sets of two tasks. The tasks within a set share a variable, access
* to which is guarded by a semaphore.
*
* Each task starts by attempting to obtain the semaphore. On obtaining a
* semaphore a task checks to ensure that the guarded variable has an expected
* value. It then clears the variable to zero before counting it back up to the
* expected value in increments of 1. After each increment the variable is checked
* to ensure it contains the value to which it was just set. When the starting
* value is again reached the task releases the semaphore giving the other task in
* the set a chance to do exactly the same thing. The starting value is high
* enough to ensure that a tick is likely to occur during the incrementing loop.
*
* An error is flagged if at any time during the process a shared variable is
* found to have a value other than that expected. Such an occurrence would
* suggest an error in the mutual exclusion mechanism by which access to the
* variable is restricted.
*
* The first set of two tasks poll their semaphore. The second set use blocking
* calls.
*
* \page SemTestC semtest.c
* \ingroup DemoFiles
* <HR>
*/
/*
Changes from V1.2.0:
+ The tasks that operate at the idle priority now use a lower expected
count than those running at a higher priority. This prevents the low
priority tasks from signaling an error because they have not been
scheduled enough time for each of them to count the shared variable to
the high value.
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than unsigned long.
Changes from V2.1.1
+ The stack size now uses configMINIMAL_STACK_SIZE.
+ String constants made file scope to decrease stack depth on 8051 port.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo app include files. */
#include "semtest.h"
#include "print.h"
/* The value to which the shared variables are counted. */
#define semtstBLOCKING_EXPECTED_VALUE ( ( unsigned long ) 0xfff )
#define semtstNON_BLOCKING_EXPECTED_VALUE ( ( unsigned long ) 0xff )
#define semtstSTACK_SIZE configMINIMAL_STACK_SIZE
#define semtstNUM_TASKS ( 4 )
#define semtstDELAY_FACTOR ( ( TickType_t ) 10 )
/* The task function as described at the top of the file. */
static void prvSemaphoreTest( void *pvParameters );
/* Structure used to pass parameters to each task. */
typedef struct SEMAPHORE_PARAMETERS
{
SemaphoreHandle_t xSemaphore;
volatile unsigned long *pulSharedVariable;
TickType_t xBlockTime;
} xSemaphoreParameters;
/* Variables used to check that all the tasks are still running without errors. */
static volatile short sCheckVariables[ semtstNUM_TASKS ] = { 0 };
static volatile short sNextCheckVariable = 0;
/* Strings to print if USE_STDIO is defined. */
const char * const pcPollingSemaphoreTaskError = "Guarded shared variable in unexpected state.\r\n";
const char * const pcSemaphoreTaskStart = "Guarded shared variable task started.\r\n";
/*-----------------------------------------------------------*/
void vStartSemaphoreTasks( unsigned portBASE_TYPE uxPriority )
{
xSemaphoreParameters *pxFirstSemaphoreParameters, *pxSecondSemaphoreParameters;
const TickType_t xBlockTime = ( TickType_t ) 100;
/* Create the structure used to pass parameters to the first two tasks. */
pxFirstSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );
if( pxFirstSemaphoreParameters != NULL )
{
/* Create the semaphore used by the first two tasks. */
vSemaphoreCreateBinary( pxFirstSemaphoreParameters->xSemaphore );
if( pxFirstSemaphoreParameters->xSemaphore != NULL )
{
/* Create the variable which is to be shared by the first two tasks. */
pxFirstSemaphoreParameters->pulSharedVariable = ( unsigned long * ) pvPortMalloc( sizeof( unsigned long ) );
/* Initialise the share variable to the value the tasks expect. */
*( pxFirstSemaphoreParameters->pulSharedVariable ) = semtstNON_BLOCKING_EXPECTED_VALUE;
/* The first two tasks do not block on semaphore calls. */
pxFirstSemaphoreParameters->xBlockTime = ( TickType_t ) 0;
/* Spawn the first two tasks. As they poll they operate at the idle priority. */
xTaskCreate( prvSemaphoreTest, "PolSEM1", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );
xTaskCreate( prvSemaphoreTest, "PolSEM2", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );
}
}
/* Do exactly the same to create the second set of tasks, only this time
provide a block time for the semaphore calls. */
pxSecondSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );
if( pxSecondSemaphoreParameters != NULL )
{
vSemaphoreCreateBinary( pxSecondSemaphoreParameters->xSemaphore );
if( pxSecondSemaphoreParameters->xSemaphore != NULL )
{
pxSecondSemaphoreParameters->pulSharedVariable = ( unsigned long * ) pvPortMalloc( sizeof( unsigned long ) );
*( pxSecondSemaphoreParameters->pulSharedVariable ) = semtstBLOCKING_EXPECTED_VALUE;
pxSecondSemaphoreParameters->xBlockTime = xBlockTime / portTICK_PERIOD_MS;
xTaskCreate( prvSemaphoreTest, "BlkSEM1", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );
xTaskCreate( prvSemaphoreTest, "BlkSEM2", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );
}
}
}
/*-----------------------------------------------------------*/
static void prvSemaphoreTest( void *pvParameters )
{
xSemaphoreParameters *pxParameters;
volatile unsigned long *pulSharedVariable, ulExpectedValue;
unsigned long ulCounter;
short sError = pdFALSE, sCheckVariableToUse;
/* See which check variable to use. sNextCheckVariable is not semaphore
protected! */
portENTER_CRITICAL();
sCheckVariableToUse = sNextCheckVariable;
sNextCheckVariable++;
portEXIT_CRITICAL();
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcSemaphoreTaskStart );
/* A structure is passed in as the parameter. This contains the shared
variable being guarded. */
pxParameters = ( xSemaphoreParameters * ) pvParameters;
pulSharedVariable = pxParameters->pulSharedVariable;
/* If we are blocking we use a much higher count to ensure loads of context
switches occur during the count. */
if( pxParameters->xBlockTime > ( TickType_t ) 0 )
{
ulExpectedValue = semtstBLOCKING_EXPECTED_VALUE;
}
else
{
ulExpectedValue = semtstNON_BLOCKING_EXPECTED_VALUE;
}
for( ;; )
{
/* Try to obtain the semaphore. */
if( xSemaphoreTake( pxParameters->xSemaphore, pxParameters->xBlockTime ) == pdPASS )
{
/* We have the semaphore and so expect any other tasks using the
shared variable to have left it in the state we expect to find
it. */
if( *pulSharedVariable != ulExpectedValue )
{
vPrintDisplayMessage( &pcPollingSemaphoreTaskError );
sError = pdTRUE;
}
/* Clear the variable, then count it back up to the expected value
before releasing the semaphore. Would expect a context switch or
two during this time. */
for( ulCounter = ( unsigned long ) 0; ulCounter <= ulExpectedValue; ulCounter++ )
{
*pulSharedVariable = ulCounter;
if( *pulSharedVariable != ulCounter )
{
if( sError == pdFALSE )
{
vPrintDisplayMessage( &pcPollingSemaphoreTaskError );
}
sError = pdTRUE;
}
}
/* Release the semaphore, and if no errors have occurred increment the check
variable. */
if( xSemaphoreGive( pxParameters->xSemaphore ) == pdFALSE )
{
vPrintDisplayMessage( &pcPollingSemaphoreTaskError );
sError = pdTRUE;
}
if( sError == pdFALSE )
{
if( sCheckVariableToUse < semtstNUM_TASKS )
{
( sCheckVariables[ sCheckVariableToUse ] )++;
}
}
/* If we have a block time then we are running at a priority higher
than the idle priority. This task takes a long time to complete
a cycle (deliberately so to test the guarding) so will be starving
out lower priority tasks. Block for some time to allow give lower
priority tasks some processor time. */
vTaskDelay( pxParameters->xBlockTime * semtstDELAY_FACTOR );
}
else
{
if( pxParameters->xBlockTime == ( TickType_t ) 0 )
{
/* We have not got the semaphore yet, so no point using the
processor. We are not blocking when attempting to obtain the
semaphore. */
taskYIELD();
}
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
portBASE_TYPE xAreSemaphoreTasksStillRunning( void )
{
static short sLastCheckVariables[ semtstNUM_TASKS ] = { 0 };
portBASE_TYPE xTask, xReturn = pdTRUE;
for( xTask = 0; xTask < semtstNUM_TASKS; xTask++ )
{
if( sLastCheckVariables[ xTask ] == sCheckVariables[ xTask ] )
{
xReturn = pdFALSE;
}
sLastCheckVariables[ xTask ] = sCheckVariables[ xTask ];
}
return xReturn;
}

View File

@ -0,0 +1,720 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This file contains some test scenarios that ensure tasks respond correctly
* to xTaskAbortDelay() calls. It also ensures tasks return the correct state
* of eBlocked when blocked indefinitely in both the case where a task is
* blocked on an object and when a task is blocked on a notification.
*/
/* Standard includes. */
#include "limits.h"
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "stream_buffer.h"
/* Demo includes. */
#include "AbortDelay.h"
/* This file can only be used if the functionality it tests is included in the
build. Remove the whole file if this is not the case. */
#if( INCLUDE_xTaskAbortDelay == 1 )
#if( INCLUDE_xTaskGetHandle != 1 )
#error This test file uses the xTaskGetHandle() API function so INCLUDE_xTaskGetHandle must be set to 1 in FreeRTOSConfig.h.
#endif
/* Task priorities. Allow these to be overridden. */
#ifndef abtCONTROLLING_PRIORITY
#define abtCONTROLLING_PRIORITY ( configMAX_PRIORITIES - 3 )
#endif
#ifndef abtBLOCKING_PRIORITY
#define abtBLOCKING_PRIORITY ( configMAX_PRIORITIES - 2 )
#endif
/* The tests that are performed. */
#define abtNOTIFY_WAIT_ABORTS 0
#define abtNOTIFY_TAKE_ABORTS 1
#define abtDELAY_ABORTS 2
#define abtDELAY_UNTIL_ABORTS 3
#define abtSEMAPHORE_TAKE_ABORTS 4
#define abtEVENT_GROUP_ABORTS 5
#define abtQUEUE_SEND_ABORTS 6
#define abtSTREAM_BUFFER_RECEIVE 7
#define abtMAX_TESTS 8
/*-----------------------------------------------------------*/
/*
* The two test tasks. The controlling task specifies which test to executed.
* More information is provided in the comments within the tasks.
*/
static void prvControllingTask( void *pvParameters );
static void prvBlockingTask( void *pvParameters );
/*
* Test functions called by the blocking task. Each function follows the same
* pattern, but the way the task blocks is different in each case.
*
* In each function three blocking calls are made. The first and third
* blocking call is expected to time out, while the middle blocking call is
* expected to be aborted by the controlling task half way through the block
* time.
*/
static void prvTestAbortingTaskNotifyWait( void );
static void prvTestAbortingTaskNotifyTake( void );
static void prvTestAbortingTaskDelay( void );
static void prvTestAbortingTaskDelayUntil( void );
static void prvTestAbortingSemaphoreTake( void );
static void prvTestAbortingEventGroupWait( void );
static void prvTestAbortingQueueSend( void );
static void prvTestAbortingStreamBufferReceive( void );
/*
* Checks the amount of time a task spent in the Blocked state is within the
* expected bounds.
*/
static void prvCheckExpectedTimeIsWithinAnAcceptableMargin( TickType_t xStartTime, TickType_t xExpectedBlockTime );
/*-----------------------------------------------------------*/
/* Used to ensure that tasks are still executing without error. */
static volatile BaseType_t xControllingCycles = 0, xBlockingCycles = 0;
static volatile BaseType_t xErrorOccurred = pdFALSE;
/* Each task needs to know the other tasks handle so they can send signals to
each other. The handle is obtained from the task's name. */
static const char *pcControllingTaskName = "AbtCtrl", *pcBlockingTaskName = "AbtBlk";
/* The maximum amount of time a task will block for. */
const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 100 );
const TickType_t xHalfMaxBlockTime = pdMS_TO_TICKS( 50 );
/* The actual block time is dependent on the priority of other tasks in the
system so the actual block time might be greater than that expected, but it
should be within an acceptable upper bound. */
const TickType_t xAllowableMargin = pdMS_TO_TICKS( 7 );
/*-----------------------------------------------------------*/
void vCreateAbortDelayTasks( void )
{
/* Create the two test tasks described above. */
xTaskCreate( prvControllingTask, pcControllingTaskName, configMINIMAL_STACK_SIZE, NULL, abtCONTROLLING_PRIORITY, NULL );
xTaskCreate( prvBlockingTask, pcBlockingTaskName, configMINIMAL_STACK_SIZE, NULL, abtBLOCKING_PRIORITY, NULL );
}
/*-----------------------------------------------------------*/
static void prvControllingTask( void *pvParameters )
{
TaskHandle_t xBlockingTask;
uint32_t ulTestToPerform = abtNOTIFY_WAIT_ABORTS;
TickType_t xTimeAtStart;
const TickType_t xStartMargin = 2UL;
/* Just to remove compiler warnings. */
( void ) pvParameters;
xBlockingTask = xTaskGetHandle( pcBlockingTaskName );
configASSERT( xBlockingTask );
for( ;; )
{
/* Tell the secondary task to perform the next test. */
xTimeAtStart = xTaskGetTickCount();
xTaskNotify( xBlockingTask, ulTestToPerform, eSetValueWithOverwrite );
/* The secondary task has a higher priority, so will now be in the
Blocked state to wait for a maximum of xMaxBlockTime. It expects that
period to complete with a timeout. It will then block for
xMaxBlockTimeAgain, but this time it expects to the block time to abort
half way through. Block until it is time to send the abort to the
secondary task. xStartMargin is used because this task takes timing
from the beginning of the test, whereas the blocking task takes timing
from the entry into the Blocked state - and as the tasks run at
different priorities, there may be some discrepancy. Also, temporarily
raise the priority of the controlling task to that of the blocking
task to minimise discrepancies. */
vTaskPrioritySet( NULL, abtBLOCKING_PRIORITY );
vTaskDelay( xMaxBlockTime + xHalfMaxBlockTime + xStartMargin );
if( xTaskAbortDelay( xBlockingTask ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
/* Reset the priority to the normal controlling priority. */
vTaskPrioritySet( NULL, abtCONTROLLING_PRIORITY );
/* Now wait to be notified that the secondary task has completed its
test. */
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
/* Did the entire test run for the expected time, which is two full
block times plus the half block time caused by calling
xTaskAbortDelay()? */
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, ( xMaxBlockTime + xMaxBlockTime + xHalfMaxBlockTime ) );
/* Move onto the next test. */
ulTestToPerform++;
if( ulTestToPerform >= abtMAX_TESTS )
{
ulTestToPerform = 0;
}
/* To indicate this task is still executing. */
xControllingCycles++;
}
}
/*-----------------------------------------------------------*/
static void prvBlockingTask( void *pvParameters )
{
TaskHandle_t xControllingTask;
uint32_t ulNotificationValue;
const uint32_t ulMax = 0xffffffffUL;
/* Just to remove compiler warnings. */
( void ) pvParameters;
xControllingTask = xTaskGetHandle( pcControllingTaskName );
configASSERT( xControllingTask );
for( ;; )
{
/* Wait to be notified of the test that is to be performed next. */
xTaskNotifyWait( 0, ulMax, &ulNotificationValue, portMAX_DELAY );
switch( ulNotificationValue )
{
case abtNOTIFY_WAIT_ABORTS:
prvTestAbortingTaskNotifyWait();
break;
case abtNOTIFY_TAKE_ABORTS:
prvTestAbortingTaskNotifyTake();
break;
case abtDELAY_ABORTS:
prvTestAbortingTaskDelay();
break;
case abtDELAY_UNTIL_ABORTS:
prvTestAbortingTaskDelayUntil();
break;
case abtSEMAPHORE_TAKE_ABORTS:
prvTestAbortingSemaphoreTake();
break;
case abtEVENT_GROUP_ABORTS:
prvTestAbortingEventGroupWait();
break;
case abtQUEUE_SEND_ABORTS:
prvTestAbortingQueueSend();
break;
case abtSTREAM_BUFFER_RECEIVE:
prvTestAbortingStreamBufferReceive();
break;
default:
/* Should not get here. */
break;
}
/* Let the primary task know the test is complete. */
xTaskNotifyGive( xControllingTask );
/* To indicate this task is still executing. */
xBlockingCycles++;
}
}
/*-----------------------------------------------------------*/
static void prvTestAbortingTaskDelayUntil( void )
{
TickType_t xTimeAtStart, xLastBlockTime;
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* Take a copy of the time as it is updated in the call to
vTaskDelayUntil() but its original value is needed to determine the actual
time spend in the Blocked state. */
xLastBlockTime = xTimeAtStart;
/* This first delay should just time out. */
vTaskDelayUntil( &xLastBlockTime, xMaxBlockTime );
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* This second delay should be aborted by the primary task half way
through. Again take a copy of the time as it is updated in the call to
vTaskDelayUntil() buts its original value is needed to determine the amount
of time actually spent in the Blocked state. */
xTimeAtStart = xTaskGetTickCount();
xLastBlockTime = xTimeAtStart;
vTaskDelayUntil( &xLastBlockTime, xMaxBlockTime );
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* As with the other tests, the third block period should not time out. */
xTimeAtStart = xTaskGetTickCount();
xLastBlockTime = xTimeAtStart;
vTaskDelayUntil( &xLastBlockTime, xMaxBlockTime );
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingTaskDelay( void )
{
TickType_t xTimeAtStart;
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
vTaskDelay( xMaxBlockTime );
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through. */
vTaskDelay( xMaxBlockTime );
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
vTaskDelay( xMaxBlockTime );
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingTaskNotifyTake( void )
{
TickType_t xTimeAtStart;
uint32_t ulReturn;
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime );
if( ulReturn != 0 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through. */
ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime );
if( ulReturn != 0 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime );
if( ulReturn != 0 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingEventGroupWait( void )
{
TickType_t xTimeAtStart;
EventGroupHandle_t xEventGroup;
EventBits_t xBitsToWaitFor = ( EventBits_t ) 0x01, xReturn;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
static StaticEventGroup_t xEventGroupBuffer;
/* Create the event group. Statically allocated memory is used so the
creation cannot fail. */
xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
}
#else
{
xEventGroup = xEventGroupCreate();
configASSERT( xEventGroup );
}
#endif
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime );
if( xReturn != 0x00 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through. */
xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime );
if( xReturn != 0x00 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime );
if( xReturn != 0x00 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Not really necessary in this case, but for completeness. */
vEventGroupDelete( xEventGroup );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingStreamBufferReceive( void )
{
TickType_t xTimeAtStart;
StreamBufferHandle_t xStreamBuffer;
EventBits_t xReturn;
const size_t xTriggerLevelBytes = ( size_t ) 1;
uint8_t uxRxData;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* Defines the memory that will actually hold the streams within the
stream buffer. */
static uint8_t ucStorageBuffer[ sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) + 1 ];
/* The variable used to hold the stream buffer structure. */
StaticStreamBuffer_t xStreamBufferStruct;
xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucStorageBuffer ),
xTriggerLevelBytes,
ucStorageBuffer,
&xStreamBufferStruct );
}
#else
{
xStreamBuffer = xStreamBufferCreate( sizeof( uint8_t ), xTriggerLevelBytes );
configASSERT( xStreamBuffer );
}
#endif
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime );
if( xReturn != 0x00 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through xMaxBlockTime. */
xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime );
if( xReturn != 0x00 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime );
if( xReturn != 0x00 )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Not really necessary in this case, but for completeness. */
vStreamBufferDelete( xStreamBuffer );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingQueueSend( void )
{
TickType_t xTimeAtStart;
BaseType_t xReturn;
const UBaseType_t xQueueLength = ( UBaseType_t ) 1;
QueueHandle_t xQueue;
uint8_t ucItemToQueue;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
static StaticQueue_t xQueueBuffer;
static uint8_t ucQueueStorage[ sizeof( uint8_t ) ];
/* Create the queue. Statically allocated memory is used so the
creation cannot fail. */
xQueue = xQueueCreateStatic( xQueueLength, sizeof( uint8_t ), ucQueueStorage, &xQueueBuffer );
}
#else
{
xQueue = xQueueCreate( xQueueLength, sizeof( uint8_t ) );
configASSERT( xQueue );
}
#endif
/* This function tests aborting when in the blocked state waiting to send,
so the queue must be full. There is only one space in the queue. */
xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime );
if( xReturn != pdPASS )
{
xErrorOccurred = pdTRUE;
}
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through. */
xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Not really necessary in this case, but for completeness. */
vQueueDelete( xQueue );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingSemaphoreTake( void )
{
TickType_t xTimeAtStart;
BaseType_t xReturn;
SemaphoreHandle_t xSemaphore;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
static StaticSemaphore_t xSemaphoreBuffer;
/* Create the semaphore. Statically allocated memory is used so the
creation cannot fail. */
xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer );
}
#else
{
xSemaphore = xSemaphoreCreateBinary();
}
#endif
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
xReturn = xSemaphoreTake( xSemaphore, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through xMaxBlockTime. */
xReturn = xSemaphoreTake( xSemaphore, portMAX_DELAY );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
xReturn = xSemaphoreTake( xSemaphore, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Not really necessary in this case, but for completeness. */
vSemaphoreDelete( xSemaphore );
}
/*-----------------------------------------------------------*/
static void prvTestAbortingTaskNotifyWait( void )
{
TickType_t xTimeAtStart;
BaseType_t xReturn;
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This first delay should just time out. */
xReturn = xTaskNotifyWait( 0, 0, NULL, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This second delay should be aborted by the primary task half way
through xMaxBlockTime. */
xReturn = xTaskNotifyWait( 0, 0, NULL, portMAX_DELAY );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime );
/* Note the time before the delay so the length of the delay is known. */
xTimeAtStart = xTaskGetTickCount();
/* This third delay should just time out again. */
xReturn = xTaskNotifyWait( 0, 0, NULL, xMaxBlockTime );
if( xReturn != pdFALSE )
{
xErrorOccurred = pdTRUE;
}
prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime );
}
/*-----------------------------------------------------------*/
static void prvCheckExpectedTimeIsWithinAnAcceptableMargin( TickType_t xStartTime, TickType_t xExpectedBlockTime )
{
TickType_t xTimeNow, xActualBlockTime;
xTimeNow = xTaskGetTickCount();
xActualBlockTime = xTimeNow - xStartTime;
/* The actual block time should not be less than the expected block time. */
if( xActualBlockTime < xExpectedBlockTime )
{
xErrorOccurred = pdTRUE;
}
/* The actual block time can be greater than the expected block time, as it
depends on the priority of the other tasks, but it should be within an
acceptable margin. */
if( xActualBlockTime > ( xExpectedBlockTime + xAllowableMargin ) )
{
xErrorOccurred = pdTRUE;
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreAbortDelayTestTasksStillRunning( void )
{
static BaseType_t xLastControllingCycleCount = 0, xLastBlockingCycleCount = 0;
BaseType_t xReturn = pdPASS;
/* Have both tasks performed at least one cycle since this function was
last called? */
if( xControllingCycles == xLastControllingCycleCount )
{
xReturn = pdFAIL;
}
if( xBlockingCycles == xLastBlockingCycleCount )
{
xReturn = pdFAIL;
}
if( xErrorOccurred == pdTRUE )
{
xReturn = pdFAIL;
}
xLastBlockingCycleCount = xBlockingCycles;
xLastControllingCycleCount = xControllingCycles;
return xReturn;
}
#endif /* INCLUDE_xTaskAbortDelay == 1 */

View File

@ -0,0 +1,290 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Creates six tasks that operate on three queues as follows:
*
* The first two tasks send and receive an incrementing number to/from a queue.
* One task acts as a producer and the other as the consumer. The consumer is a
* higher priority than the producer and is set to block on queue reads. The queue
* only has space for one item - as soon as the producer posts a message on the
* queue the consumer will unblock, pre-empt the producer, and remove the item.
*
* The second two tasks work the other way around. Again the queue used only has
* enough space for one item. This time the consumer has a lower priority than the
* producer. The producer will try to post on the queue blocking when the queue is
* full. When the consumer wakes it will remove the item from the queue, causing
* the producer to unblock, pre-empt the consumer, and immediately re-fill the
* queue.
*
* The last two tasks use the same queue producer and consumer functions. This time the queue has
* enough space for lots of items and the tasks operate at the same priority. The
* producer will execute, placing items into the queue. The consumer will start
* executing when either the queue becomes full (causing the producer to block) or
* a context switch occurs (tasks of the same priority will time slice).
*
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo program include files. */
#include "BlockQ.h"
#define blckqSTACK_SIZE configMINIMAL_STACK_SIZE
#define blckqNUM_TASK_SETS ( 3 )
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This example cannot be used if dynamic allocation is not allowed.
#endif
/* Structure used to pass parameters to the blocking queue tasks. */
typedef struct BLOCKING_QUEUE_PARAMETERS
{
QueueHandle_t xQueue; /*< The queue to be used by the task. */
TickType_t xBlockTime; /*< The block time to use on queue reads/writes. */
volatile short *psCheckVariable; /*< Incremented on each successful cycle to check the task is still running. */
} xBlockingQueueParameters;
/* Task function that creates an incrementing number and posts it on a queue. */
static portTASK_FUNCTION_PROTO( vBlockingQueueProducer, pvParameters );
/* Task function that removes the incrementing number from a queue and checks that
it is the expected number. */
static portTASK_FUNCTION_PROTO( vBlockingQueueConsumer, pvParameters );
/* Variables which are incremented each time an item is removed from a queue, and
found to be the expected value.
These are used to check that the tasks are still running. */
static volatile short sBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 };
/* Variable which are incremented each time an item is posted on a queue. These
are used to check that the tasks are still running. */
static volatile short sBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 };
/*-----------------------------------------------------------*/
void vStartBlockingQueueTasks( UBaseType_t uxPriority )
{
xBlockingQueueParameters *pxQueueParameters1, *pxQueueParameters2;
xBlockingQueueParameters *pxQueueParameters3, *pxQueueParameters4;
xBlockingQueueParameters *pxQueueParameters5, *pxQueueParameters6;
const UBaseType_t uxQueueSize1 = 1, uxQueueSize5 = 5;
const TickType_t xBlockTime = pdMS_TO_TICKS( ( TickType_t ) 1000 );
const TickType_t xDontBlock = ( TickType_t ) 0;
/* Create the first two tasks as described at the top of the file. */
/* First create the structure used to pass parameters to the consumer tasks. */
pxQueueParameters1 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
/* Create the queue used by the first two tasks to pass the incrementing number.
Pass a pointer to the queue in the parameter structure. */
pxQueueParameters1->xQueue = xQueueCreate( uxQueueSize1, ( UBaseType_t ) sizeof( uint16_t ) );
/* The consumer is created first so gets a block time as described above. */
pxQueueParameters1->xBlockTime = xBlockTime;
/* Pass in the variable that this task is going to increment so we can check it
is still running. */
pxQueueParameters1->psCheckVariable = &( sBlockingConsumerCount[ 0 ] );
/* Create the structure used to pass parameters to the producer task. */
pxQueueParameters2 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
/* Pass the queue to this task also, using the parameter structure. */
pxQueueParameters2->xQueue = pxQueueParameters1->xQueue;
/* The producer is not going to block - as soon as it posts the consumer will
wake and remove the item so the producer should always have room to post. */
pxQueueParameters2->xBlockTime = xDontBlock;
/* Pass in the variable that this task is going to increment so we can check
it is still running. */
pxQueueParameters2->psCheckVariable = &( sBlockingProducerCount[ 0 ] );
/* Note the producer has a lower priority than the consumer when the tasks are
spawned. */
xTaskCreate( vBlockingQueueConsumer, "QConsB1", blckqSTACK_SIZE, ( void * ) pxQueueParameters1, uxPriority, NULL );
xTaskCreate( vBlockingQueueProducer, "QProdB2", blckqSTACK_SIZE, ( void * ) pxQueueParameters2, tskIDLE_PRIORITY, NULL );
/* Create the second two tasks as described at the top of the file. This uses
the same mechanism but reverses the task priorities. */
pxQueueParameters3 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters3->xQueue = xQueueCreate( uxQueueSize1, ( UBaseType_t ) sizeof( uint16_t ) );
pxQueueParameters3->xBlockTime = xDontBlock;
pxQueueParameters3->psCheckVariable = &( sBlockingProducerCount[ 1 ] );
pxQueueParameters4 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters4->xQueue = pxQueueParameters3->xQueue;
pxQueueParameters4->xBlockTime = xBlockTime;
pxQueueParameters4->psCheckVariable = &( sBlockingConsumerCount[ 1 ] );
xTaskCreate( vBlockingQueueConsumer, "QConsB3", blckqSTACK_SIZE, ( void * ) pxQueueParameters3, tskIDLE_PRIORITY, NULL );
xTaskCreate( vBlockingQueueProducer, "QProdB4", blckqSTACK_SIZE, ( void * ) pxQueueParameters4, uxPriority, NULL );
/* Create the last two tasks as described above. The mechanism is again just
the same. This time both parameter structures are given a block time. */
pxQueueParameters5 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters5->xQueue = xQueueCreate( uxQueueSize5, ( UBaseType_t ) sizeof( uint16_t ) );
pxQueueParameters5->xBlockTime = xBlockTime;
pxQueueParameters5->psCheckVariable = &( sBlockingProducerCount[ 2 ] );
pxQueueParameters6 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) );
pxQueueParameters6->xQueue = pxQueueParameters5->xQueue;
pxQueueParameters6->xBlockTime = xBlockTime;
pxQueueParameters6->psCheckVariable = &( sBlockingConsumerCount[ 2 ] );
xTaskCreate( vBlockingQueueProducer, "QProdB5", blckqSTACK_SIZE, ( void * ) pxQueueParameters5, tskIDLE_PRIORITY, NULL );
xTaskCreate( vBlockingQueueConsumer, "QConsB6", blckqSTACK_SIZE, ( void * ) pxQueueParameters6, tskIDLE_PRIORITY, NULL );
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vBlockingQueueProducer, pvParameters )
{
uint16_t usValue = 0;
xBlockingQueueParameters *pxQueueParameters;
short sErrorEverOccurred = pdFALSE;
pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters;
for( ;; )
{
if( xQueueSend( pxQueueParameters->xQueue, ( void * ) &usValue, pxQueueParameters->xBlockTime ) != pdPASS )
{
sErrorEverOccurred = pdTRUE;
}
else
{
/* We have successfully posted a message, so increment the variable
used to check we are still running. */
if( sErrorEverOccurred == pdFALSE )
{
( *pxQueueParameters->psCheckVariable )++;
}
/* Increment the variable we are going to post next time round. The
consumer will expect the numbers to follow in numerical order. */
++usValue;
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vBlockingQueueConsumer, pvParameters )
{
uint16_t usData, usExpectedValue = 0;
xBlockingQueueParameters *pxQueueParameters;
short sErrorEverOccurred = pdFALSE;
pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters;
for( ;; )
{
if( xQueueReceive( pxQueueParameters->xQueue, &usData, pxQueueParameters->xBlockTime ) == pdPASS )
{
if( usData != usExpectedValue )
{
/* Catch-up. */
usExpectedValue = usData;
sErrorEverOccurred = pdTRUE;
}
else
{
/* We have successfully received a message, so increment the
variable used to check we are still running. */
if( sErrorEverOccurred == pdFALSE )
{
( *pxQueueParameters->psCheckVariable )++;
}
/* Increment the value we expect to remove from the queue next time
round. */
++usExpectedValue;
}
#if configUSE_PREEMPTION == 0
{
if( pxQueueParameters->xBlockTime == 0 )
{
taskYIELD();
}
}
#endif
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreBlockingQueuesStillRunning( void )
{
static short sLastBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 };
static short sLastBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 };
BaseType_t xReturn = pdPASS, xTasks;
/* Not too worried about mutual exclusion on these variables as they are 16
bits and we are only reading them. We also only care to see if they have
changed or not.
Loop through each check variable to and return pdFALSE if any are found not
to have changed since the last call. */
for( xTasks = 0; xTasks < blckqNUM_TASK_SETS; xTasks++ )
{
if( sBlockingConsumerCount[ xTasks ] == sLastBlockingConsumerCount[ xTasks ] )
{
xReturn = pdFALSE;
}
sLastBlockingConsumerCount[ xTasks ] = sBlockingConsumerCount[ xTasks ];
if( sBlockingProducerCount[ xTasks ] == sLastBlockingProducerCount[ xTasks ] )
{
xReturn = pdFALSE;
}
sLastBlockingProducerCount[ xTasks ] = sBlockingProducerCount[ xTasks ];
}
return xReturn;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,727 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This file defines one of the more complex set of demo/test tasks. They are
* designed to stress test the queue implementation though pseudo simultaneous
* multiple reads and multiple writes from both tasks of varying priority and
* interrupts. The interrupts are prioritised such to ensure that nesting
* occurs (for those ports that support it).
*
* The test ensures that, while being accessed from three tasks and two
* interrupts, all the data sent to the queues is also received from
* the same queue, and that no duplicate items are either sent or received.
* The tests also ensure that a low priority task is never able to successfully
* read from or write to a queue when a task of higher priority is attempting
* the same operation.
*/
/* Standard includes. */
#include <string.h>
/* SafeRTOS includes. */
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
/* Demo app includes. */
#include "IntQueue.h"
#include "IntQueueTimer.h"
#if( INCLUDE_eTaskGetState != 1 )
#error INCLUDE_eTaskGetState must be set to 1 in FreeRTOSConfig.h to use this demo file.
#endif
/* Priorities used by test tasks. */
#ifndef intqHIGHER_PRIORITY
#define intqHIGHER_PRIORITY ( configMAX_PRIORITIES - 2 )
#endif
#define intqLOWER_PRIORITY ( tskIDLE_PRIORITY )
/* The number of values to send/receive before checking that all values were
processed as expected. */
#define intqNUM_VALUES_TO_LOG ( 200 )
#define intqSHORT_DELAY ( 140 )
/* The value by which the value being sent to or received from a queue should
increment past intqNUM_VALUES_TO_LOG before we check that all values have been
sent/received correctly. This is done to ensure that all tasks and interrupts
accessing the queue have completed their accesses with the
intqNUM_VALUES_TO_LOG range. */
#define intqVALUE_OVERRUN ( 50 )
/* The delay used by the polling task. A short delay is used for code
coverage. */
#define intqONE_TICK_DELAY ( 1 )
/* Each task and interrupt is given a unique identifier. This value is used to
identify which task sent or received each value. The identifier is also used
to distinguish between two tasks that are running the same task function. */
#define intqHIGH_PRIORITY_TASK1 ( ( UBaseType_t ) 1 )
#define intqHIGH_PRIORITY_TASK2 ( ( UBaseType_t ) 2 )
#define intqLOW_PRIORITY_TASK ( ( UBaseType_t ) 3 )
#define intqFIRST_INTERRUPT ( ( UBaseType_t ) 4 )
#define intqSECOND_INTERRUPT ( ( UBaseType_t ) 5 )
#define intqQUEUE_LENGTH ( ( UBaseType_t ) 10 )
/* At least intqMIN_ACCEPTABLE_TASK_COUNT values should be sent to/received
from each queue by each task, otherwise an error is detected. */
#define intqMIN_ACCEPTABLE_TASK_COUNT ( 5 )
/* Send the next value to the queue that is normally empty. This is called
from within the interrupts. */
#define timerNORMALLY_EMPTY_TX() \
if( xQueueIsQueueFullFromISR( xNormallyEmptyQueue ) != pdTRUE ) \
{ \
UBaseType_t uxSavedInterruptStatus; \
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); \
{ \
uxValueForNormallyEmptyQueue++; \
if( xQueueSendFromISR( xNormallyEmptyQueue, ( void * ) &uxValueForNormallyEmptyQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
{ \
uxValueForNormallyEmptyQueue--; \
} \
} \
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \
} \
/* Send the next value to the queue that is normally full. This is called
from within the interrupts. */
#define timerNORMALLY_FULL_TX() \
if( xQueueIsQueueFullFromISR( xNormallyFullQueue ) != pdTRUE ) \
{ \
UBaseType_t uxSavedInterruptStatus; \
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); \
{ \
uxValueForNormallyFullQueue++; \
if( xQueueSendFromISR( xNormallyFullQueue, ( void * ) &uxValueForNormallyFullQueue, &xHigherPriorityTaskWoken ) != pdPASS ) \
{ \
uxValueForNormallyFullQueue--; \
} \
} \
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \
} \
/* Receive a value from the normally empty queue. This is called from within
an interrupt. */
#define timerNORMALLY_EMPTY_RX() \
if( xQueueReceiveFromISR( xNormallyEmptyQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) != pdPASS ) \
{ \
prvQueueAccessLogError( __LINE__ ); \
} \
else \
{ \
prvRecordValue_NormallyEmpty( uxRxedValue, intqSECOND_INTERRUPT ); \
}
/* Receive a value from the normally full queue. This is called from within
an interrupt. */
#define timerNORMALLY_FULL_RX() \
if( xQueueReceiveFromISR( xNormallyFullQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) == pdPASS ) \
{ \
prvRecordValue_NormallyFull( uxRxedValue, intqSECOND_INTERRUPT ); \
} \
/*-----------------------------------------------------------*/
/* The two queues used by the test. */
static QueueHandle_t xNormallyEmptyQueue, xNormallyFullQueue;
/* Variables used to detect a stall in one of the tasks. */
static volatile UBaseType_t uxHighPriorityLoops1 = 0, uxHighPriorityLoops2 = 0, uxLowPriorityLoops1 = 0, uxLowPriorityLoops2 = 0;
/* Any unexpected behaviour sets xErrorStatus to fail and log the line that
caused the error in xErrorLine. */
static BaseType_t xErrorStatus = pdPASS;
static volatile UBaseType_t xErrorLine = ( UBaseType_t ) 0;
/* Used for sequencing between tasks. */
static BaseType_t xWasSuspended = pdFALSE;
/* The values that are sent to the queues. An incremented value is sent each
time to each queue. */
static volatile UBaseType_t uxValueForNormallyEmptyQueue = 0, uxValueForNormallyFullQueue = 0;
/* A handle to some of the tasks is required so they can be suspended/resumed. */
TaskHandle_t xHighPriorityNormallyEmptyTask1, xHighPriorityNormallyEmptyTask2, xHighPriorityNormallyFullTask1, xHighPriorityNormallyFullTask2;
/* When a value is received in a queue the value is ticked off in the array
the array position of the value is set to a the identifier of the task or
interrupt that accessed the queue. This way missing or duplicate values can be
detected. */
static uint8_t ucNormallyEmptyReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
static uint8_t ucNormallyFullReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 };
/* The test tasks themselves. */
static void prvLowerPriorityNormallyEmptyTask( void *pvParameters );
static void prvLowerPriorityNormallyFullTask( void *pvParameters );
static void prvHigherPriorityNormallyEmptyTask( void *pvParameters );
static void prv1stHigherPriorityNormallyFullTask( void *pvParameters );
static void prv2ndHigherPriorityNormallyFullTask( void *pvParameters );
/* Used to mark the positions within the ucNormallyEmptyReceivedValues and
ucNormallyFullReceivedValues arrays, while checking for duplicates. */
static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue, UBaseType_t uxSource );
static void prvRecordValue_NormallyFull( UBaseType_t uxValue, UBaseType_t uxSource );
/* Logs the line on which an error occurred. */
static void prvQueueAccessLogError( UBaseType_t uxLine );
/*-----------------------------------------------------------*/
void vStartInterruptQueueTasks( void )
{
/* Start the test tasks. */
xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H1QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask1 );
xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H2QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask2 );
xTaskCreate( prvLowerPriorityNormallyEmptyTask, "L1QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
xTaskCreate( prv1stHigherPriorityNormallyFullTask, "H1QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask1 );
xTaskCreate( prv2ndHigherPriorityNormallyFullTask, "H2QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask2 );
xTaskCreate( prvLowerPriorityNormallyFullTask, "L2QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL );
/* Create the queues that are accessed by multiple tasks and multiple
interrupts. */
xNormallyFullQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
xNormallyEmptyQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) );
/* vQueueAddToRegistry() adds the queue to the queue registry, if one is
in use. The queue registry is provided as a means for kernel aware
debuggers to locate queues and has no purpose if a kernel aware debugger
is not being used. The call to vQueueAddToRegistry() will be removed
by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
defined to be less than 1. */
vQueueAddToRegistry( xNormallyFullQueue, "NormallyFull" );
vQueueAddToRegistry( xNormallyEmptyQueue, "NormallyEmpty" );
}
/*-----------------------------------------------------------*/
static void prvRecordValue_NormallyFull( UBaseType_t uxValue, UBaseType_t uxSource )
{
if( uxValue < intqNUM_VALUES_TO_LOG )
{
/* We don't expect to receive the same value twice, so if the value
has already been marked as received an error has occurred. */
if( ucNormallyFullReceivedValues[ uxValue ] != 0x00 )
{
prvQueueAccessLogError( __LINE__ );
}
/* Log that this value has been received. */
ucNormallyFullReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
}
}
/*-----------------------------------------------------------*/
static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue, UBaseType_t uxSource )
{
if( uxValue < intqNUM_VALUES_TO_LOG )
{
/* We don't expect to receive the same value twice, so if the value
has already been marked as received an error has occurred. */
if( ucNormallyEmptyReceivedValues[ uxValue ] != 0x00 )
{
prvQueueAccessLogError( __LINE__ );
}
/* Log that this value has been received. */
ucNormallyEmptyReceivedValues[ uxValue ] = ( uint8_t ) uxSource;
}
}
/*-----------------------------------------------------------*/
static void prvQueueAccessLogError( UBaseType_t uxLine )
{
/* Latch the line number that caused the error. */
xErrorLine = uxLine;
xErrorStatus = pdFAIL;
}
/*-----------------------------------------------------------*/
static void prvHigherPriorityNormallyEmptyTask( void *pvParameters )
{
UBaseType_t uxRxed, ux, uxTask1, uxTask2, uxInterrupts, uxErrorCount1 = 0, uxErrorCount2 = 0;
/* The timer should not be started until after the scheduler has started.
More than one task is running this code so we check the parameter value
to determine which task should start the timer. */
if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
{
vInitialiseTimerForIntQueueTest();
}
for( ;; )
{
/* Block waiting to receive a value from the normally empty queue.
Interrupts will write to the queue so we should receive a value. */
if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqSHORT_DELAY ) != pdPASS )
{
prvQueueAccessLogError( __LINE__ );
}
else
{
/* Note which value was received so we can check all expected
values are received and no values are duplicated. */
prvRecordValue_NormallyEmpty( uxRxed, ( UBaseType_t ) pvParameters );
}
/* Ensure the other task running this code gets a chance to execute. */
taskYIELD();
if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 )
{
/* Have we received all the expected values? */
if( uxValueForNormallyEmptyQueue > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
{
vTaskSuspend( xHighPriorityNormallyEmptyTask2 );
uxTask1 = 0;
uxTask2 = 0;
uxInterrupts = 0;
/* Loop through the array, checking that both tasks have
placed values into the array, and that no values are missing.
Start at 1 as we expect position 0 to be unused. */
for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
{
if( ucNormallyEmptyReceivedValues[ ux ] == 0 )
{
/* A value is missing. */
prvQueueAccessLogError( __LINE__ );
}
else
{
if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK1 )
{
/* Value was placed into the array by task 1. */
uxTask1++;
}
else if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK2 )
{
/* Value was placed into the array by task 2. */
uxTask2++;
}
else if( ucNormallyEmptyReceivedValues[ ux ] == intqSECOND_INTERRUPT )
{
uxInterrupts++;
}
}
}
if( uxTask1 < intqMIN_ACCEPTABLE_TASK_COUNT )
{
/* Only task 2 seemed to log any values. */
uxErrorCount1++;
if( uxErrorCount1 > 2 )
{
prvQueueAccessLogError( __LINE__ );
}
}
else
{
uxErrorCount1 = 0;
}
if( uxTask2 < intqMIN_ACCEPTABLE_TASK_COUNT )
{
/* Only task 1 seemed to log any values. */
uxErrorCount2++;
if( uxErrorCount2 > 2 )
{
prvQueueAccessLogError( __LINE__ );
}
}
else
{
uxErrorCount2 = 0;
}
if( uxInterrupts == 0 )
{
prvQueueAccessLogError( __LINE__ );
}
/* Clear the array again, ready to start a new cycle. */
memset( ucNormallyEmptyReceivedValues, 0x00, sizeof( ucNormallyEmptyReceivedValues ) );
uxHighPriorityLoops1++;
uxValueForNormallyEmptyQueue = 0;
/* Suspend ourselves, allowing the lower priority task to
actually receive something from the queue. Until now it
will have been prevented from doing so by the higher
priority tasks. The lower priority task will resume us
if it receives something. We will then resume the other
higher priority task. */
vTaskSuspend( NULL );
vTaskResume( xHighPriorityNormallyEmptyTask2 );
}
}
}
}
/*-----------------------------------------------------------*/
static void prvLowerPriorityNormallyEmptyTask( void *pvParameters )
{
UBaseType_t uxValue, uxRxed;
/* The parameters are not being used so avoid compiler warnings. */
( void ) pvParameters;
for( ;; )
{
if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqONE_TICK_DELAY ) != errQUEUE_EMPTY )
{
/* A value should only be obtained when the high priority task is
suspended. */
if( eTaskGetState( xHighPriorityNormallyEmptyTask1 ) != eSuspended )
{
prvQueueAccessLogError( __LINE__ );
}
prvRecordValue_NormallyEmpty( uxRxed, intqLOW_PRIORITY_TASK );
/* Wake the higher priority task again. */
vTaskResume( xHighPriorityNormallyEmptyTask1 );
uxLowPriorityLoops1++;
}
else
{
/* Raise our priority while we send so we can preempt the higher
priority task, and ensure we get the Tx value into the queue. */
vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );
portENTER_CRITICAL();
{
uxValueForNormallyEmptyQueue++;
uxValue = uxValueForNormallyEmptyQueue;
}
portEXIT_CRITICAL();
if( xQueueSend( xNormallyEmptyQueue, &uxValue, portMAX_DELAY ) != pdPASS )
{
prvQueueAccessLogError( __LINE__ );
}
vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
}
}
}
/*-----------------------------------------------------------*/
static void prv1stHigherPriorityNormallyFullTask( void *pvParameters )
{
UBaseType_t uxValueToTx, ux, uxInterrupts;
/* The parameters are not being used so avoid compiler warnings. */
( void ) pvParameters;
/* Make sure the queue starts full or near full. >> 1 as there are two
high priority tasks. */
for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
{
portENTER_CRITICAL();
{
uxValueForNormallyFullQueue++;
uxValueToTx = uxValueForNormallyFullQueue;
}
portEXIT_CRITICAL();
xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
}
for( ;; )
{
portENTER_CRITICAL();
{
uxValueForNormallyFullQueue++;
uxValueToTx = uxValueForNormallyFullQueue;
}
portEXIT_CRITICAL();
if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
{
/* intqHIGH_PRIORITY_TASK2 is never suspended so we would not
expect it to ever time out. */
prvQueueAccessLogError( __LINE__ );
}
/* Allow the other task running this code to run. */
taskYIELD();
/* Have all the expected values been sent to the queue? */
if( uxValueToTx > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) )
{
/* Make sure the other high priority task completes its send of
any values below intqNUM_VALUE_TO_LOG. */
vTaskDelay( intqSHORT_DELAY );
vTaskSuspend( xHighPriorityNormallyFullTask2 );
if( xWasSuspended == pdTRUE )
{
/* We would have expected the other high priority task to have
set this back to false by now. */
prvQueueAccessLogError( __LINE__ );
}
/* Set the suspended flag so an error is not logged if the other
task recognises a time out when it is unsuspended. */
xWasSuspended = pdTRUE;
/* Check interrupts are also sending. */
uxInterrupts = 0U;
/* Start at 1 as we expect position 0 to be unused. */
for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ )
{
if( ucNormallyFullReceivedValues[ ux ] == 0 )
{
/* A value was missing. */
prvQueueAccessLogError( __LINE__ );
}
else if( ucNormallyFullReceivedValues[ ux ] == intqSECOND_INTERRUPT )
{
uxInterrupts++;
}
}
if( uxInterrupts == 0 )
{
/* No writes from interrupts were found. Are interrupts
actually running? */
prvQueueAccessLogError( __LINE__ );
}
/* Reset the array ready for the next cycle. */
memset( ucNormallyFullReceivedValues, 0x00, sizeof( ucNormallyFullReceivedValues ) );
uxHighPriorityLoops2++;
uxValueForNormallyFullQueue = 0;
/* Suspend ourselves, allowing the lower priority task to
actually receive something from the queue. Until now it
will have been prevented from doing so by the higher
priority tasks. The lower priority task will resume us
if it receives something. We will then resume the other
higher priority task. */
vTaskSuspend( NULL );
vTaskResume( xHighPriorityNormallyFullTask2 );
}
}
}
/*-----------------------------------------------------------*/
static void prv2ndHigherPriorityNormallyFullTask( void *pvParameters )
{
UBaseType_t uxValueToTx, ux;
/* The parameters are not being used so avoid compiler warnings. */
( void ) pvParameters;
/* Make sure the queue starts full or near full. >> 1 as there are two
high priority tasks. */
for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ )
{
portENTER_CRITICAL();
{
uxValueForNormallyFullQueue++;
uxValueToTx = uxValueForNormallyFullQueue;
}
portEXIT_CRITICAL();
xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY );
}
for( ;; )
{
portENTER_CRITICAL();
{
uxValueForNormallyFullQueue++;
uxValueToTx = uxValueForNormallyFullQueue;
}
portEXIT_CRITICAL();
if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS )
{
if( xWasSuspended != pdTRUE )
{
/* It is ok to time out if the task has been suspended. */
prvQueueAccessLogError( __LINE__ );
}
}
xWasSuspended = pdFALSE;
taskYIELD();
}
}
/*-----------------------------------------------------------*/
static void prvLowerPriorityNormallyFullTask( void *pvParameters )
{
UBaseType_t uxValue, uxTxed = 9999;
/* The parameters are not being used so avoid compiler warnings. */
( void ) pvParameters;
for( ;; )
{
if( xQueueSend( xNormallyFullQueue, &uxTxed, intqONE_TICK_DELAY ) != errQUEUE_FULL )
{
/* Should only succeed when the higher priority task is suspended */
if( eTaskGetState( xHighPriorityNormallyFullTask1 ) != eSuspended )
{
prvQueueAccessLogError( __LINE__ );
}
vTaskResume( xHighPriorityNormallyFullTask1 );
uxLowPriorityLoops2++;
}
else
{
/* Raise our priority while we receive so we can preempt the higher
priority task, and ensure we get the value from the queue. */
vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 );
if( xQueueReceive( xNormallyFullQueue, &uxValue, portMAX_DELAY ) != pdPASS )
{
prvQueueAccessLogError( __LINE__ );
}
else
{
prvRecordValue_NormallyFull( uxValue, intqLOW_PRIORITY_TASK );
}
vTaskPrioritySet( NULL, intqLOWER_PRIORITY );
}
}
}
/*-----------------------------------------------------------*/
BaseType_t xFirstTimerHandler( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
UBaseType_t uxRxedValue;
static UBaseType_t uxNextOperation = 0;
/* Called from a timer interrupt. Perform various read and write
accesses on the queues. */
uxNextOperation++;
if( uxNextOperation & ( UBaseType_t ) 0x01 )
{
timerNORMALLY_EMPTY_TX();
timerNORMALLY_EMPTY_TX();
timerNORMALLY_EMPTY_TX();
}
else
{
timerNORMALLY_FULL_RX();
timerNORMALLY_FULL_RX();
timerNORMALLY_FULL_RX();
}
return xHigherPriorityTaskWoken;
}
/*-----------------------------------------------------------*/
BaseType_t xSecondTimerHandler( void )
{
UBaseType_t uxRxedValue;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
static UBaseType_t uxNextOperation = 0;
/* Called from a timer interrupt. Perform various read and write
accesses on the queues. */
uxNextOperation++;
if( uxNextOperation & ( UBaseType_t ) 0x01 )
{
timerNORMALLY_EMPTY_TX();
timerNORMALLY_EMPTY_TX();
timerNORMALLY_EMPTY_RX();
timerNORMALLY_EMPTY_RX();
}
else
{
timerNORMALLY_FULL_RX();
timerNORMALLY_FULL_TX();
timerNORMALLY_FULL_TX();
timerNORMALLY_FULL_TX();
}
return xHigherPriorityTaskWoken;
}
/*-----------------------------------------------------------*/
BaseType_t xAreIntQueueTasksStillRunning( void )
{
static UBaseType_t uxLastHighPriorityLoops1 = 0, uxLastHighPriorityLoops2 = 0, uxLastLowPriorityLoops1 = 0, uxLastLowPriorityLoops2 = 0;
/* xErrorStatus can be set outside of this function. This function just
checks that all the tasks are still cycling. */
if( uxHighPriorityLoops1 == uxLastHighPriorityLoops1 )
{
/* The high priority 1 task has stalled. */
prvQueueAccessLogError( __LINE__ );
}
uxLastHighPriorityLoops1 = uxHighPriorityLoops1;
if( uxHighPriorityLoops2 == uxLastHighPriorityLoops2 )
{
/* The high priority 2 task has stalled. */
prvQueueAccessLogError( __LINE__ );
}
uxLastHighPriorityLoops2 = uxHighPriorityLoops2;
if( uxLowPriorityLoops1 == uxLastLowPriorityLoops1 )
{
/* The low priority 1 task has stalled. */
prvQueueAccessLogError( __LINE__ );
}
uxLastLowPriorityLoops1 = uxLowPriorityLoops1;
if( uxLowPriorityLoops2 == uxLastLowPriorityLoops2 )
{
/* The low priority 2 task has stalled. */
prvQueueAccessLogError( __LINE__ );
}
uxLastLowPriorityLoops2 = uxLowPriorityLoops2;
return xErrorStatus;
}

View File

@ -0,0 +1,525 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Demonstrates and tests mutexes being used from an interrupt.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo program include files. */
#include "IntSemTest.h"
/*-----------------------------------------------------------*/
/* The priorities of the test tasks. */
#define intsemMASTER_PRIORITY ( tskIDLE_PRIORITY )
#define intsemSLAVE_PRIORITY ( tskIDLE_PRIORITY + 1 )
/* The rate at which the tick hook will give the mutex. */
#define intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 )
/* A block time of 0 means 'don't block'. */
#define intsemNO_BLOCK 0
/* The maximum count value for the counting semaphore given from an
interrupt. */
#define intsemMAX_COUNT 3
/*-----------------------------------------------------------*/
/*
* The master is a task that receives a mutex that is given from an interrupt -
* although generally mutexes should not be used given in interrupts (and
* definitely never taken in an interrupt) there are some circumstances when it
* may be desirable.
*
* The slave task is just used by the master task to force priority inheritance
* on a mutex that is shared between the master and the slave - which is a
* separate mutex to that given by the interrupt.
*/
static void vInterruptMutexSlaveTask( void *pvParameters );
static void vInterruptMutexMasterTask( void *pvParameters );
/*
* A test whereby the master takes the shared and interrupt mutexes in that
* order, then gives them back in the same order, ensuring the priority
* inheritance is behaving as expected at each step.
*/
static void prvTakeAndGiveInTheSameOrder( void );
/*
* A test whereby the master takes the shared and interrupt mutexes in that
* order, then gives them back in the opposite order to which they were taken,
* ensuring the priority inheritance is behaving as expected at each step.
*/
static void prvTakeAndGiveInTheOppositeOrder( void );
/*
* A simple task that interacts with an interrupt using a counting semaphore,
* primarily for code coverage purposes.
*/
static void vInterruptCountingSemaphoreTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* Flag that will be latched to pdTRUE should any unexpected behaviour be
detected in any of the tasks. */
static volatile BaseType_t xErrorDetected = pdFALSE;
/* Counters that are incremented on each cycle of a test. This is used to
detect a stalled task - a test that is no longer running. */
static volatile uint32_t ulMasterLoops = 0, ulCountingSemaphoreLoops = 0;
/* Handles of the test tasks that must be accessed from other test tasks. */
static TaskHandle_t xSlaveHandle;
/* A mutex which is given from an interrupt - although generally mutexes should
not be used given in interrupts (and definitely never taken in an interrupt)
there are some circumstances when it may be desirable. */
static SemaphoreHandle_t xISRMutex = NULL;
/* A counting semaphore which is given from an interrupt. */
static SemaphoreHandle_t xISRCountingSemaphore = NULL;
/* A mutex which is shared between the master and slave tasks - the master
does both sharing of this mutex with the slave and receiving a mutex from the
interrupt. */
static SemaphoreHandle_t xMasterSlaveMutex = NULL;
/* Flag that allows the master task to control when the interrupt gives or does
not give the mutex. There is no mutual exclusion on this variable, but this is
only test code and it should be fine in the 32=bit test environment. */
static BaseType_t xOkToGiveMutex = pdFALSE, xOkToGiveCountingSemaphore = pdFALSE;
/* Used to coordinate timing between tasks and the interrupt. */
const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );
/*-----------------------------------------------------------*/
void vStartInterruptSemaphoreTasks( void )
{
/* Create the semaphores that are given from an interrupt. */
xISRMutex = xSemaphoreCreateMutex();
configASSERT( xISRMutex );
xISRCountingSemaphore = xSemaphoreCreateCounting( intsemMAX_COUNT, 0 );
configASSERT( xISRCountingSemaphore );
/* Create the mutex that is shared between the master and slave tasks (the
master receives a mutex from an interrupt and shares a mutex with the
slave. */
xMasterSlaveMutex = xSemaphoreCreateMutex();
configASSERT( xMasterSlaveMutex );
/* Create the tasks that share mutexes between then and with interrupts. */
xTaskCreate( vInterruptMutexSlaveTask, "IntMuS", configMINIMAL_STACK_SIZE, NULL, intsemSLAVE_PRIORITY, &xSlaveHandle );
xTaskCreate( vInterruptMutexMasterTask, "IntMuM", configMINIMAL_STACK_SIZE, NULL, intsemMASTER_PRIORITY, NULL );
/* Create the task that blocks on the counting semaphore. */
xTaskCreate( vInterruptCountingSemaphoreTask, "IntCnt", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
}
/*-----------------------------------------------------------*/
static void vInterruptMutexMasterTask( void *pvParameters )
{
/* Just to avoid compiler warnings. */
( void ) pvParameters;
for( ;; )
{
prvTakeAndGiveInTheSameOrder();
/* Ensure not to starve out other tests. */
ulMasterLoops++;
vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );
prvTakeAndGiveInTheOppositeOrder();
/* Ensure not to starve out other tests. */
ulMasterLoops++;
vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );
}
}
/*-----------------------------------------------------------*/
static void prvTakeAndGiveInTheSameOrder( void )
{
/* Ensure the slave is suspended, and that this task is running at the
lower priority as expected as the start conditions. */
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Take the semaphore that is shared with the slave. */
if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* This task now has the mutex. Unsuspend the slave so it too
attempts to take the mutex. */
vTaskResume( xSlaveHandle );
/* The slave has the higher priority so should now have executed and
blocked on the semaphore. */
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked );
}
#endif /* INCLUDE_eTaskGetState */
/* This task should now have inherited the priority of the slave
task. */
if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Now wait a little longer than the time between ISR gives to also
obtain the ISR mutex. */
xOkToGiveMutex = pdTRUE;
if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
xOkToGiveMutex = pdFALSE;
/* Attempting to take again immediately should fail as the mutex is
already held. */
if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL )
{
xErrorDetected = pdTRUE;
}
/* Should still be at the priority of the slave task. */
if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Give back the ISR semaphore to ensure the priority is not
disinherited as the shared mutex (which the higher priority task is
attempting to obtain) is still held. */
if( xSemaphoreGive( xISRMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Finally give back the shared mutex. This time the higher priority
task should run before this task runs again - so this task should have
disinherited the priority and the higher priority task should be in the
suspended state again. */
if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )
{
xErrorDetected = pdTRUE;
}
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
/* Reset the mutex ready for the next round. */
xQueueReset( xISRMutex );
}
/*-----------------------------------------------------------*/
static void prvTakeAndGiveInTheOppositeOrder( void )
{
/* Ensure the slave is suspended, and that this task is running at the
lower priority as expected as the start conditions. */
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Take the semaphore that is shared with the slave. */
if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* This task now has the mutex. Unsuspend the slave so it too
attempts to take the mutex. */
vTaskResume( xSlaveHandle );
/* The slave has the higher priority so should now have executed and
blocked on the semaphore. */
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked );
}
#endif /* INCLUDE_eTaskGetState */
/* This task should now have inherited the priority of the slave
task. */
if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Now wait a little longer than the time between ISR gives to also
obtain the ISR mutex. */
xOkToGiveMutex = pdTRUE;
if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
xOkToGiveMutex = pdFALSE;
/* Attempting to take again immediately should fail as the mutex is
already held. */
if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL )
{
xErrorDetected = pdTRUE;
}
/* Should still be at the priority of the slave task. */
if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Give back the shared semaphore to ensure the priority is not disinherited
as the ISR mutex is still held. The higher priority slave task should run
before this task runs again. */
if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* Should still be at the priority of the slave task as this task still
holds one semaphore (this is a simplification in the priority inheritance
mechanism. */
if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Give back the ISR semaphore, which should result in the priority being
disinherited as it was the last mutex held. */
if( xSemaphoreGive( xISRMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Reset the mutex ready for the next round. */
xQueueReset( xISRMutex );
}
/*-----------------------------------------------------------*/
static void vInterruptMutexSlaveTask( void *pvParameters )
{
/* Just to avoid compiler warnings. */
( void ) pvParameters;
for( ;; )
{
/* This task starts by suspending itself so when it executes can be
controlled by the master task. */
vTaskSuspend( NULL );
/* This task will execute when the master task already holds the mutex.
Attempting to take the mutex will place this task in the Blocked
state. */
if( xSemaphoreTake( xMasterSlaveMutex, portMAX_DELAY ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
}
}
/*-----------------------------------------------------------*/
static void vInterruptCountingSemaphoreTask( void *pvParameters )
{
BaseType_t xCount;
const TickType_t xDelay = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) * ( intsemMAX_COUNT + 1 );
( void ) pvParameters;
for( ;; )
{
/* Expect to start with the counting semaphore empty. */
if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 )
{
xErrorDetected = pdTRUE;
}
/* Wait until it is expected that the interrupt will have filled the
counting semaphore. */
xOkToGiveCountingSemaphore = pdTRUE;
vTaskDelay( xDelay );
xOkToGiveCountingSemaphore = pdFALSE;
/* Now it is expected that the counting semaphore is full. */
if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != intsemMAX_COUNT )
{
xErrorDetected = pdTRUE;
}
if( uxQueueSpacesAvailable( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 )
{
xErrorDetected = pdTRUE;
}
ulCountingSemaphoreLoops++;
/* Expect to be able to take the counting semaphore intsemMAX_COUNT
times. A block time of 0 is used as the semaphore should already be
there. */
xCount = 0;
while( xSemaphoreTake( xISRCountingSemaphore, 0 ) == pdPASS )
{
xCount++;
}
if( xCount != intsemMAX_COUNT )
{
xErrorDetected = pdTRUE;
}
/* Now raise the priority of this task so it runs immediately that the
semaphore is given from the interrupt. */
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
/* Block to wait for the semaphore to be given from the interrupt. */
xOkToGiveCountingSemaphore = pdTRUE;
xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY );
xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY );
xOkToGiveCountingSemaphore = pdFALSE;
/* Reset the priority so as not to disturbe other tests too much. */
vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
ulCountingSemaphoreLoops++;
}
}
/*-----------------------------------------------------------*/
void vInterruptSemaphorePeriodicTest( void )
{
static TickType_t xLastGiveTime = 0;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
TickType_t xTimeNow;
/* No mutual exclusion on xOkToGiveMutex, but this is only test code (and
only executed on a 32-bit architecture) so ignore that in this case. */
xTimeNow = xTaskGetTickCountFromISR();
if( ( ( TickType_t ) ( xTimeNow - xLastGiveTime ) ) >= pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) )
{
configASSERT( xISRMutex );
if( xOkToGiveMutex != pdFALSE )
{
/* Null is used as the second parameter in this give, and non-NULL
in the other gives for code coverage reasons. */
xSemaphoreGiveFromISR( xISRMutex, NULL );
/* Second give attempt should fail. */
configASSERT( xSemaphoreGiveFromISR( xISRMutex, &xHigherPriorityTaskWoken ) == pdFAIL );
}
if( xOkToGiveCountingSemaphore != pdFALSE )
{
xSemaphoreGiveFromISR( xISRCountingSemaphore, &xHigherPriorityTaskWoken );
}
xLastGiveTime = xTimeNow;
}
/* Remove compiler warnings about the value being set but not used. */
( void ) xHigherPriorityTaskWoken;
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreInterruptSemaphoreTasksStillRunning( void )
{
static uint32_t ulLastMasterLoopCounter = 0, ulLastCountingSemaphoreLoops = 0;
/* If the demo tasks are running then it is expected that the loop counters
will have changed since this function was last called. */
if( ulLastMasterLoopCounter == ulMasterLoops )
{
xErrorDetected = pdTRUE;
}
ulLastMasterLoopCounter = ulMasterLoops;
if( ulLastCountingSemaphoreLoops == ulCountingSemaphoreLoops )
{
xErrorDetected = pdTRUE;
}
ulLastCountingSemaphoreLoops = ulCountingSemaphoreLoops++;
/* Errors detected in the task itself will have latched xErrorDetected
to true. */
return ( BaseType_t ) !xErrorDetected;
}

View File

@ -0,0 +1,330 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* An example that mimics a message buffer being used to pass data from one core
* to another. The core that sends the data is referred to as core A. The core
* that receives the data is referred to as core B. The task implemented by
* prvCoreATask() runs on core A. Two instances of the task implemented by
* prvCoreBTasks() run on core B. prvCoreATask() sends messages via message
* buffers to both instances of prvCoreBTasks(), one message buffer per channel.
* A third message buffer is used to pass the handle of the message buffer
* written to by core A to an interrupt service routine that is triggered by
* core A but executes on core B.
*
* The example relies on the FreeRTOS provided default implementation of
* sbSEND_COMPLETED() being overridden by an implementation in FreeRTOSConfig.h
* that writes the handle of the message buffer that contains data into the
* control message buffer, then generates an interrupt in core B. The necessary
* implementation is provided in this file and can be enabled by adding the
* following to FreeRTOSConfig.h:
*
* #define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer )
*
* Core to core communication via message buffer requires the message buffers
* to be at an address known to both cores within shared memory.
*
* Note that, while this example uses three message buffers, the same
* functionality can be implemented using a single message buffer by using the
* same design pattern described on the link below for queues, but using message
* buffers instead. It is actually simpler with a message buffer as variable
* length data can be written into the message buffer directly:
* http://www.freertos.org/Pend-on-multiple-rtos-objects.html#alternative_design_pattern
*/
/* Standard includes. */
#include "stdio.h"
#include "string.h"
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "message_buffer.h"
/* Demo app includes. */
#include "MessageBufferAMP.h"
/* Enough for 3 4 byte pointers, including the additional 4 bytes per message
overhead of message buffers. */
#define mbaCONTROL_MESSAGE_BUFFER_SIZE ( 24 )
/* Enough four 4 8 byte strings, plus the additional 4 bytes per message
overhead of message buffers. */
#define mbaTASK_MESSAGE_BUFFER_SIZE ( 60 )
/* The number of instances of prvCoreBTasks that are created. */
#define mbaNUMBER_OF_CORE_B_TASKS 2
/* A block time of 0 simply means, don't block. */
#define mbaDONT_BLOCK 0
/* Macro that mimics an interrupt service routine executing by simply calling
the routine inline. */
#define mbaGENERATE_CORE_B_INTERRUPT() prvCoreBInterruptHandler()
/*-----------------------------------------------------------*/
/*
* Implementation of the task that, on a real dual core device, would run on
* core A and send message to tasks running on core B.
*/
static void prvCoreATask( void *pvParameters );
/*
* Implementation of the task that, on a real dual core device, would run on
* core B and receive message from core A. The demo creates two instances of
* this task.
*/
static void prvCoreBTasks( void *pvParameters );
/*
* The function that, on a real dual core device, would handle inter-core
* interrupts, but in this case is just called inline.
*/
static void prvCoreBInterruptHandler( void );
/*-----------------------------------------------------------*/
/* The message buffers used to pass data from core A to core B. */
static MessageBufferHandle_t xCoreBMessageBuffers[ mbaNUMBER_OF_CORE_B_TASKS ];
/* The control message buffer. This is used to pass the handle of the message
message buffer that holds application data into the core to core interrupt
service routine. */
static MessageBufferHandle_t xControlMessageBuffer;
/* Counters used to indicate to the check that the tasks are still executing. */
static uint32_t ulCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ];
/* Set to pdFALSE if any errors are detected. Used to inform the check task
that something might be wrong. */
BaseType_t xDemoStatus = pdPASS;
/*-----------------------------------------------------------*/
void vStartMessageBufferAMPTasks( configSTACK_DEPTH_TYPE xStackSize )
{
BaseType_t x;
xControlMessageBuffer = xMessageBufferCreate( mbaCONTROL_MESSAGE_BUFFER_SIZE );
xTaskCreate( prvCoreATask, /* The function that implements the task. */
"AMPCoreA", /* Human readable name for the task. */
xStackSize, /* Stack size (in words!). */
NULL, /* Task parameter is not used. */
tskIDLE_PRIORITY, /* The priority at which the task is created. */
NULL ); /* No use for the task handle. */
for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )
{
xCoreBMessageBuffers[ x ] = xMessageBufferCreate( mbaTASK_MESSAGE_BUFFER_SIZE );
configASSERT( xCoreBMessageBuffers[ x ] );
/* Pass the loop counter into the created task using the task's
parameter. The task then uses the value as an index into the
ulCycleCounters and xCoreBMessageBuffers arrays. */
xTaskCreate( prvCoreBTasks,
"AMPCoreB1",
xStackSize,
( void * ) x,
tskIDLE_PRIORITY + 1,
NULL );
}
}
/*-----------------------------------------------------------*/
static void prvCoreATask( void *pvParameters )
{
BaseType_t x;
uint32_t ulNextValue = 0;
const TickType_t xDelay = pdMS_TO_TICKS( 250 );
char cString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */
/* Remove warning about unused parameters. */
( void ) pvParameters;
for( ;; )
{
/* Create the next string to send. The value is incremented on each
loop iteration, and the length of the string changes as the number of
digits in the value increases. */
sprintf( cString, "%lu", ( unsigned long ) ulNextValue );
/* Send the value from this (pseudo) Core A to the tasks on the (pseudo)
Core B via the message buffers. This will result in sbSEND_COMPLETED()
being executed, which in turn will write the handle of the message
buffer written to into xControlMessageBuffer then generate an interrupt
in core B. */
for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )
{
xMessageBufferSend( /* The message buffer to write to. */
xCoreBMessageBuffers[ x ],
/* The source of the data to send. */
( void * ) cString,
/* The length of the data to send. */
strlen( cString ),
/* The block time, should the buffer be full. */
mbaDONT_BLOCK );
}
/* Delay before repeating with a different and potentially different
length string. */
vTaskDelay( xDelay );
ulNextValue++;
}
}
/*-----------------------------------------------------------*/
static void prvCoreBTasks( void *pvParameters )
{
BaseType_t x;
size_t xReceivedBytes;
uint32_t ulNextValue = 0;
char cExpectedString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */
char cReceivedString[ 15 ];
/* The index into the xCoreBMessageBuffers and ulLoopCounter arrays is
passed into this task using the task's parameter. */
x = ( BaseType_t ) pvParameters;
configASSERT( x < mbaNUMBER_OF_CORE_B_TASKS );
for( ;; )
{
/* Create the string that is expected to be received this time round. */
sprintf( cExpectedString, "%lu", ( unsigned long ) ulNextValue );
/* Wait to receive the next message from core A. */
memset( cReceivedString, 0x00, sizeof( cReceivedString ) );
xReceivedBytes = xMessageBufferReceive( /* The message buffer to receive from. */
xCoreBMessageBuffers[ x ],
/* Location to store received data. */
cReceivedString,
/* Maximum number of bytes to receive. */
sizeof( cReceivedString ),
/* Ticks to wait if buffer is empty. */
portMAX_DELAY );
/* Check the number of bytes received was as expected. */
configASSERT( xReceivedBytes == strlen( cExpectedString ) );
( void ) xReceivedBytes; /* Incase configASSERT() is not defined. */
/* If the received string matches that expected then increment the loop
counter so the check task knows this task is still running. */
if( strcmp( cReceivedString, cExpectedString ) == 0 )
{
( ulCycleCounters[ x ] )++;
}
else
{
xDemoStatus = pdFAIL;
}
/* Expect the next string in sequence the next time around. */
ulNextValue++;
}
}
/*-----------------------------------------------------------*/
/* Called by the reimplementation of sbSEND_COMPLETED(), which can be defined
as follows in FreeRTOSConfig.h:
#define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer )
*/
void vGenerateCoreBInterrupt( void * xUpdatedMessageBuffer )
{
MessageBufferHandle_t xUpdatedBuffer = ( MessageBufferHandle_t ) xUpdatedMessageBuffer;
/* If sbSEND_COMPLETED() has been implemented as above, then this function
is called from within xMessageBufferSend(). As this function also calls
xMessageBufferSend() itself it is necessary to guard against a recursive
call. If the message buffer just updated is the message buffer written to
by this function, then this is a recursive call, and the function can just
exit without taking further action. */
if( xUpdatedBuffer != xControlMessageBuffer )
{
/* Use xControlMessageBuffer to pass the handle of the message buffer
written to by core A to the interrupt handler about to be generated in
core B. */
xMessageBufferSend( xControlMessageBuffer, &xUpdatedBuffer, sizeof( xUpdatedBuffer ), mbaDONT_BLOCK );
/* This is where the interrupt would be generated. In this case it is
not a genuine interrupt handler that executes, just a standard function
call. */
mbaGENERATE_CORE_B_INTERRUPT();
}
}
/*-----------------------------------------------------------*/
/* Handler for the interrupts that are triggered on core A but execute on core
B. */
static void prvCoreBInterruptHandler( void )
{
MessageBufferHandle_t xUpdatedMessageBuffer;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* xControlMessageBuffer contains the handle of the message buffer that
contains data. */
if( xMessageBufferReceive( xControlMessageBuffer,
&xUpdatedMessageBuffer,
sizeof( xUpdatedMessageBuffer ),
mbaDONT_BLOCK ) == sizeof( xUpdatedMessageBuffer ) )
{
/* Call the API function that sends a notification to any task that is
blocked on the xUpdatedMessageBuffer message buffer waiting for data to
arrive. */
xMessageBufferSendCompletedFromISR( xUpdatedMessageBuffer, &xHigherPriorityTaskWoken );
}
/* Normal FreeRTOS yield from interrupt semantics, where
xHigherPriorityTaskWoken is initialzed to pdFALSE and will then get set to
pdTRUE if the interrupt safe API unblocks a task that has a priority above
that of the currently executing task. */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/
BaseType_t xAreMessageBufferAMPTasksStillRunning( void )
{
static uint32_t ulLastCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ] = { 0 };
BaseType_t x;
/* Called by the check task to determine the health status of the tasks
implemented in this demo. */
for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )
{
if( ulLastCycleCounters[ x ] == ulCycleCounters[ x ] )
{
xDemoStatus = pdFAIL;
}
else
{
ulLastCycleCounters[ x ] = ulCycleCounters[ x ];
}
}
return xDemoStatus;
}

View File

@ -0,0 +1,851 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/* Standard includes. */
#include "stdio.h"
#include "string.h"
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "message_buffer.h"
/* Demo app includes. */
#include "MessageBufferDemo.h"
/* The number of bytes of storage in the message buffers used in this test. */
#define mbMESSAGE_BUFFER_LENGTH_BYTES ( ( size_t ) 50 )
/* The number of additional bytes used to store the length of each message. */
#define mbBYTES_TO_STORE_MESSAGE_LENGTH ( sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) )
/* Start and end ASCII characters used in messages sent to the buffers. */
#define mbASCII_SPACE 32
#define mbASCII_TILDA 126
/* Defines the number of tasks to create in this test and demo. */
#define mbNUMBER_OF_ECHO_CLIENTS ( 2 )
#define mbNUMBER_OF_SENDER_TASKS ( 2 )
/* Priority of the test tasks. The send and receive go from low to high
priority tasks, and from high to low priority tasks. */
#define mbLOWER_PRIORITY ( tskIDLE_PRIORITY )
#define mbHIGHER_PRIORITY ( tskIDLE_PRIORITY + 1 )
/* Block times used when sending and receiving from the message buffers. */
#define mbRX_TX_BLOCK_TIME pdMS_TO_TICKS( 125UL )
/* A block time of 0 means "don't block". */
#define mbDONT_BLOCK ( 0 )
/*-----------------------------------------------------------*/
/*
* Performs various tests that do not require multiple tasks to interact.
*/
static void prvSingleTaskTests( MessageBufferHandle_t xMessageBuffer );
/*
* Tests sending and receiving various lengths of messages via a message buffer.
* The echo client sends the messages to the echo server, which then sends the
* message back to the echo client which, checks it receives exactly what it
* sent.
*/
static void prvEchoClient( void *pvParameters );
static void prvEchoServer( void *pvParameters );
/*
* Tasks that send and receive to a message buffer at a low priority and without
* blocking, so the send and receive functions interleave in time as the tasks
* are switched in and out.
*/
static void prvNonBlockingReceiverTask( void *pvParameters );
static void prvNonBlockingSenderTask( void *pvParameters );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
/* This file tests both statically and dynamically allocated message buffers.
Allocate the structures and buffers to be used by the statically allocated
objects, which get used in the echo tests. */
static void prvReceiverTask( void *pvParameters );
static void prvSenderTask( void *pvParameters );
static StaticMessageBuffer_t xStaticMessageBuffers[ mbNUMBER_OF_ECHO_CLIENTS ];
static uint8_t ucBufferStorage[ mbNUMBER_OF_SENDER_TASKS ][ mbMESSAGE_BUFFER_LENGTH_BYTES + 1 ];
static uint32_t ulSenderLoopCounters[ mbNUMBER_OF_SENDER_TASKS ] = { 0 };
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
/* The buffers used by the echo client and server tasks. */
typedef struct ECHO_MESSAGE_BUFFERS
{
/* Handles to the data structures that describe the message buffers. */
MessageBufferHandle_t xEchoClientBuffer;
MessageBufferHandle_t xEchoServerBuffer;
} EchoMessageBuffers_t;
static uint32_t ulEchoLoopCounters[ mbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
/* The non-blocking tasks monitor their operation, and if no errors have been
found, increment ulNonBlockingRxCounter. xAreMessageBufferTasksStillRunning()
then checks ulNonBlockingRxCounter and only returns pdPASS if
ulNonBlockingRxCounter is still incrementing. */
static uint32_t ulNonBlockingRxCounter = 0;
/* A message that is longer than the buffer, parts of which are written to the
message buffer to test writing different lengths at different offsets. */
static const char *pc55ByteString = "One two three four five six seven eight nine ten eleve";
/* Remember the required stack size so tasks can be created at run time (after
initialisation time. */
static configSTACK_DEPTH_TYPE xBlockingStackSize = 0;
/*-----------------------------------------------------------*/
void vStartMessageBufferTasks( configSTACK_DEPTH_TYPE xStackSize )
{
MessageBufferHandle_t xMessageBuffer;
xBlockingStackSize = ( xStackSize + ( xStackSize >> 1U ) );
/* The echo servers sets up the message buffers before creating the echo
client tasks. One set of tasks has the server as the higher priority, and
the other has the client as the higher priority. */
xTaskCreate( prvEchoServer, "1EchoServer", xBlockingStackSize, NULL, mbHIGHER_PRIORITY, NULL );
xTaskCreate( prvEchoServer, "2EchoServer", xBlockingStackSize, NULL, mbLOWER_PRIORITY, NULL );
/* The non blocking tasks run continuously and will interleave with each
other, so must be created at the lowest priority. The message buffer they
use is created and passed in using the task's parameter. */
xMessageBuffer = xMessageBufferCreate( mbMESSAGE_BUFFER_LENGTH_BYTES );
xTaskCreate( prvNonBlockingReceiverTask, "NonBlkRx", xStackSize, ( void * ) xMessageBuffer, tskIDLE_PRIORITY, NULL );
xTaskCreate( prvNonBlockingSenderTask, "NonBlkTx", xStackSize, ( void * ) xMessageBuffer, tskIDLE_PRIORITY, NULL );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* The sender tasks set up the message buffers before creating the
receiver tasks. Priorities must be 0 and 1 as the priority is used to
index into the xStaticMessageBuffers and ucBufferStorage arrays. */
xTaskCreate( prvSenderTask, "1Sender", xBlockingStackSize, NULL, mbHIGHER_PRIORITY, NULL );
xTaskCreate( prvSenderTask, "2Sender", xBlockingStackSize, NULL, mbLOWER_PRIORITY, NULL );
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
}
/*-----------------------------------------------------------*/
static void prvSingleTaskTests( MessageBufferHandle_t xMessageBuffer )
{
size_t xReturned, xItem, xExpectedSpace, xNextLength;
const size_t xMax6ByteMessages = mbMESSAGE_BUFFER_LENGTH_BYTES / ( 6 + mbBYTES_TO_STORE_MESSAGE_LENGTH );
const size_t x6ByteLength = 6, x17ByteLength = 17;
uint8_t *pucFullBuffer, *pucData, *pucReadData;
TickType_t xTimeBeforeCall, xTimeAfterCall;
const TickType_t xBlockTime = pdMS_TO_TICKS( 25 ), xAllowableMargin = pdMS_TO_TICKS( 3 );
UBaseType_t uxOriginalPriority;
/* Remove warning in case configASSERT() is not defined. */
( void ) xAllowableMargin;
/* To minimise stack and heap usage a full size buffer is allocated from
the heap, then buffers which hold smaller amounts of data are overlayed
with the larger buffer - just make sure not to use both at once!. */
pucFullBuffer = pvPortMalloc( mbMESSAGE_BUFFER_LENGTH_BYTES );
configASSERT( pucFullBuffer );
pucData = pucFullBuffer;
pucReadData = pucData + x17ByteLength;
/* Nothing has been added or removed yet, so expect the free space to be
exactly as created and the length of the next message to be 0. */
xExpectedSpace = xMessageBufferSpaceAvailable( xMessageBuffer );
configASSERT( xExpectedSpace == mbMESSAGE_BUFFER_LENGTH_BYTES );
configASSERT( xMessageBufferIsEmpty( xMessageBuffer ) == pdTRUE );
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == 0 );
/* In case configASSERT() is not define. */
( void ) xExpectedSpace;
( void ) xNextLength;
/* The buffer is 50 bytes long. When an item is added to the buffer an
additional 4 bytes are added to hold the item's size. That means adding
6 bytes to the buffer will actually add 10 bytes to the buffer. Therefore,
with a 50 byte buffer, a maximum of 5 6 bytes items can be added before the
buffer is completely full. NOTE: The numbers in this paragraph assume
sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) == 4. */
for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
{
configASSERT( xMessageBufferIsFull( xMessageBuffer ) == pdFALSE );
/* Generate recognisable data to write to the buffer. This is just
ascii characters that shows which loop iteration the data was written
in. The 'FromISR' version is used to give it some exercise as a block
time is not used. That requires the call to be in a critical section
so this code can also run on FreeRTOS ports that do not support
interrupt nesting (and so don't have interrupt safe critical
sections).*/
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
taskENTER_CRITICAL();
{
xReturned = xMessageBufferSendFromISR( xMessageBuffer, ( void * ) pucData, x6ByteLength, NULL );
}
taskEXIT_CRITICAL();
configASSERT( xReturned == x6ByteLength );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* The space in the buffer will have reduced by the amount of user data
written into the buffer and the amount of space used to store the length
of the data written into the buffer. */
xExpectedSpace -= ( x6ByteLength + mbBYTES_TO_STORE_MESSAGE_LENGTH );
xReturned = xMessageBufferSpaceAvailable( xMessageBuffer );
configASSERT( xReturned == xExpectedSpace );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Only 6 byte messages are written. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == x6ByteLength );
( void ) xNextLength; /* In case configASSERT() is not defined. */
}
/* Now the buffer should be full, and attempting to add anything will should
fail. */
configASSERT( xMessageBufferIsFull( xMessageBuffer ) == pdTRUE );
xReturned = xMessageBufferSend( xMessageBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), mbDONT_BLOCK );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Adding with a timeout should also fail after the appropriate time. The
priority is temporarily boosted in this part of the test to keep the
allowable margin to a minimum. */
uxOriginalPriority = uxTaskPriorityGet( NULL );
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
xTimeBeforeCall = xTaskGetTickCount();
xReturned = xMessageBufferSend( xMessageBuffer, ( void * ) pucData, sizeof( pucData[ 0 ] ), xBlockTime );
xTimeAfterCall = xTaskGetTickCount();
vTaskPrioritySet( NULL, uxOriginalPriority );
configASSERT( ( xTimeAfterCall - xTimeBeforeCall ) >= xBlockTime );
configASSERT( ( xTimeAfterCall - xTimeBeforeCall ) < ( xBlockTime + xAllowableMargin ) );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) xTimeBeforeCall;
( void ) xTimeAfterCall;
/* The buffer is now full of data in the form "000000", "111111", etc. Make
sure the data is read out as expected. */
for( xItem = 0; xItem < xMax6ByteMessages; xItem++ )
{
/* Generate the data that is expected to be read out for this loop
iteration. */
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x6ByteLength );
/* Try reading the message into a buffer that is too small. The message
should remain in the buffer. */
xReturned = xMessageBufferReceive( xMessageBuffer, ( void * ) pucReadData, x6ByteLength - 1, mbDONT_BLOCK );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Should still be at least one 6 byte message still available. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == x6ByteLength );
( void ) xNextLength; /* In case configASSERT() is not defined. */
/* Read the next 6 bytes out. The 'FromISR' version is used to give it
some exercise as a block time is not used. THa requires the code to be
in a critical section so this test can be run with FreeRTOS ports that
do not support interrupt nesting (and therefore don't have interrupt
safe critical sections). */
taskENTER_CRITICAL();
{
xReturned = xMessageBufferReceiveFromISR( xMessageBuffer, ( void * ) pucReadData, x6ByteLength, NULL );
}
taskEXIT_CRITICAL();
configASSERT( xReturned == x6ByteLength );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Does the data read out match that expected? */
configASSERT( memcmp( ( void * ) pucData, ( void * ) pucReadData, x6ByteLength ) == 0 );
/* The space in the buffer will have increased by the amount of user
data read from into the buffer and the amount of space used to store the
length of the data read into the buffer. */
xExpectedSpace += ( x6ByteLength + mbBYTES_TO_STORE_MESSAGE_LENGTH );
xReturned = xMessageBufferSpaceAvailable( xMessageBuffer );
configASSERT( xReturned == xExpectedSpace );
( void ) xReturned; /* In case configASSERT() is not defined. */
}
/* The buffer should be empty again. */
configASSERT( xMessageBufferIsEmpty( xMessageBuffer ) == pdTRUE );
xExpectedSpace = xMessageBufferSpaceAvailable( xMessageBuffer );
configASSERT( xExpectedSpace == mbMESSAGE_BUFFER_LENGTH_BYTES );
( void ) xExpectedSpace; /* In case configASSERT() is not defined. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == 0 );
( void ) xNextLength; /* In case configASSERT() is not defined. */
/* Reading with a timeout should also fail after the appropriate time. The
priority is temporarily boosted in this part of the test to keep the
allowable margin to a minimum. */
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
xTimeBeforeCall = xTaskGetTickCount();
xReturned = xMessageBufferReceive( xMessageBuffer, ( void * ) pucReadData, x6ByteLength, xBlockTime );
xTimeAfterCall = xTaskGetTickCount();
vTaskPrioritySet( NULL, uxOriginalPriority );
configASSERT( ( xTimeAfterCall - xTimeBeforeCall ) >= xBlockTime );
configASSERT( ( xTimeAfterCall - xTimeBeforeCall ) < ( xBlockTime + xAllowableMargin ) );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) xTimeBeforeCall;
( void ) xTimeAfterCall;
/* In the next loop 17 bytes are written to then read out on each iteration.
The expected length variable is always used after 17 bytes have been written
into the buffer - the length of the message is also written, making a total
of 21 bytes consumed for each 17 byte message. */
xExpectedSpace = mbMESSAGE_BUFFER_LENGTH_BYTES - ( x17ByteLength + mbBYTES_TO_STORE_MESSAGE_LENGTH );
/* Reading and writing 17 bytes at a time will result in 21 bytes being
written into the buffer, and as 50 is not divisible by 21, writing multiple
times will cause the data to wrap in the buffer.*/
for( xItem = 0; xItem < 100; xItem++ )
{
/* Generate recognisable data to write to the queue. This is just
ascii characters that shows which loop iteration the data was written
in. */
memset( ( void * ) pucData, ( ( int ) '0' ) + ( int ) xItem, x17ByteLength );
xReturned = xMessageBufferSend( xMessageBuffer, ( void * ) pucData, x17ByteLength, mbDONT_BLOCK );
configASSERT( xReturned == x17ByteLength );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Only 17 byte messages are written. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == x17ByteLength );
( void ) xNextLength; /* In case configASSERT() is not defined. */
/* The space in the buffer will have reduced by the amount of user data
written into the buffer and the amount of space used to store the length
of the data written into the buffer. */
xReturned = xMessageBufferSpaceAvailable( xMessageBuffer );
configASSERT( xReturned == xExpectedSpace );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Read the 17 bytes out again. */
xReturned = xMessageBufferReceive( xMessageBuffer, ( void * ) pucReadData, x17ByteLength, mbDONT_BLOCK );
configASSERT( xReturned == x17ByteLength );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Does the data read out match that expected? */
configASSERT( memcmp( ( void * ) pucData, ( void * ) pucReadData, x17ByteLength ) == 0 );
/* Don't expect any messages to be available as the data was read out
again. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == 0 );
( void ) xNextLength; /* In case configASSERT() is not defined. */
}
/* The buffer should be empty again. */
configASSERT( xMessageBufferIsEmpty( xMessageBuffer ) == pdTRUE );
xExpectedSpace = xMessageBufferSpaceAvailable( xMessageBuffer );
configASSERT( xExpectedSpace == mbMESSAGE_BUFFER_LENGTH_BYTES );
/* Cannot write within sizeof( size_t ) (assumed to be 4 bytes in this test)
bytes of the full 50 bytes, as that would not leave space for the four bytes
taken by the data length. */
xReturned = xMessageBufferSend( xMessageBuffer, ( const void * ) pc55ByteString, mbMESSAGE_BUFFER_LENGTH_BYTES, mbDONT_BLOCK );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
#ifndef configMESSAGE_BUFFER_LENGTH_TYPE
{
/* The following will fail if configMESSAGE_BUFFER_LENGTH_TYPE is set
to a non 32-bit type. */
xReturned = xMessageBufferSend( xMessageBuffer, ( const void * ) pc55ByteString, mbMESSAGE_BUFFER_LENGTH_BYTES - 1, mbDONT_BLOCK );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
xReturned = xMessageBufferSend( xMessageBuffer, ( const void * ) pc55ByteString, mbMESSAGE_BUFFER_LENGTH_BYTES - 2, mbDONT_BLOCK );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
xReturned = xMessageBufferSend( xMessageBuffer, ( const void * ) pc55ByteString, mbMESSAGE_BUFFER_LENGTH_BYTES - 3, mbDONT_BLOCK );
configASSERT( xReturned == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
}
#endif
/* Don't expect any messages to be available as the above were too large to
get written. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == 0 );
( void ) xNextLength; /* In case configASSERT() is not defined. */
/* Can write mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ) bytes though. */
xReturned = xMessageBufferSend( xMessageBuffer, ( const void * ) pc55ByteString, mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ), mbDONT_BLOCK );
configASSERT( xReturned == mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ) );
( void ) xReturned; /* In case configASSERT() is not defined. */
xNextLength = xMessageBufferNextLengthBytes( xMessageBuffer );
configASSERT( xNextLength == ( mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) );
( void ) xNextLength; /* In case configASSERT() is not defined. */
xReturned = xMessageBufferReceive( xMessageBuffer, ( void * ) pucFullBuffer, mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ), mbDONT_BLOCK );
configASSERT( xReturned == ( mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) );
( void ) xReturned; /* In case configASSERT() is not defined. */
configASSERT( memcmp( ( const void * ) pucFullBuffer, pc55ByteString, mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) == 0 );
/* Clean up. */
vPortFree( pucFullBuffer );
xMessageBufferReset( xMessageBuffer );
}
/*-----------------------------------------------------------*/
static void prvNonBlockingSenderTask( void *pvParameters )
{
MessageBufferHandle_t xMessageBuffer;
int32_t iDataToSend = 0;
size_t xStringLength;
const int32_t iMaxValue = 1500;
char cTxString[ 12 ]; /* Large enough to hold a 32 number in ASCII. */
/* In this case the message buffer has already been created and is passed
into the task using the task's parameter. */
xMessageBuffer = ( MessageBufferHandle_t ) pvParameters;
/* Create a string from an incrementing number. The length of the
string will increase and decrease as the value of the number increases
then overflows. */
memset( cTxString, 0x00, sizeof( cTxString ) );
sprintf( cTxString, "%d", ( int ) iDataToSend );
xStringLength = strlen( cTxString );
for( ;; )
{
/* Doesn't block so calls can interleave with the non-blocking
receives performed by prvNonBlockingReceiverTask(). */
if( xMessageBufferSend( xMessageBuffer, ( void * ) cTxString, strlen( cTxString ), mbDONT_BLOCK ) == xStringLength )
{
iDataToSend++;
if( iDataToSend > iMaxValue )
{
/* The value sent is reset back to 0 to ensure the string being sent
does not remain at the same length for too long. */
iDataToSend = 0;
}
/* Create the next string. */
memset( cTxString, 0x00, sizeof( cTxString ) );
sprintf( cTxString, "%d", ( int ) iDataToSend );
xStringLength = strlen( cTxString );
}
}
}
/*-----------------------------------------------------------*/
static void prvNonBlockingReceiverTask( void *pvParameters )
{
MessageBufferHandle_t xMessageBuffer;
BaseType_t xNonBlockingReceiveError = pdFALSE;
int32_t iDataToSend = 0;
size_t xStringLength, xReceiveLength;
const int32_t iMaxValue = 1500;
char cExpectedString[ 12 ]; /* Large enough to hold a 32 number in ASCII. */
char cRxString[ 12 ];
/* In this case the message buffer has already been created and is passed
into the task using the task's parameter. */
xMessageBuffer = ( MessageBufferHandle_t ) pvParameters;
/* Create a string from an incrementing number. The length of the
string will increase and decrease as the value of the number increases
then overflows. This should always match the string sent to the buffer by
the non blocking sender task. */
memset( cExpectedString, 0x00, sizeof( cExpectedString ) );
memset( cRxString, 0x00, sizeof( cRxString ) );
sprintf( cExpectedString, "%d", ( int ) iDataToSend );
xStringLength = strlen( cExpectedString );
for( ;; )
{
/* Doesn't block so calls can interleave with the non-blocking
receives performed by prvNonBlockingReceiverTask(). */
xReceiveLength = xMessageBufferReceive( xMessageBuffer, ( void * ) cRxString, sizeof( cRxString ), mbDONT_BLOCK );
/* Should only ever receive no data is available, or the expected
length of data is available. */
if( ( xReceiveLength != 0 ) && ( xReceiveLength != xStringLength ) )
{
xNonBlockingReceiveError = pdTRUE;
}
if( xReceiveLength == xStringLength )
{
/* Ensure the received data was that expected, then generate the
next expected string. */
if( strcmp( cRxString, cExpectedString ) != 0 )
{
xNonBlockingReceiveError = pdTRUE;
}
iDataToSend++;
if( iDataToSend > iMaxValue )
{
/* The value sent is reset back to 0 to ensure the string being sent
does not remain at the same length for too long. */
iDataToSend = 0;
}
memset( cExpectedString, 0x00, sizeof( cExpectedString ) );
memset( cRxString, 0x00, sizeof( cRxString ) );
sprintf( cExpectedString, "%d", ( int ) iDataToSend );
xStringLength = strlen( cExpectedString );
if( xNonBlockingReceiveError == pdFALSE )
{
/* No errors detected so increment the counter that lets the
check task know this test is still functioning correctly. */
ulNonBlockingRxCounter++;
}
}
}
}
/*-----------------------------------------------------------*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
static void prvSenderTask( void *pvParameters )
{
MessageBufferHandle_t xMessageBuffer, xTempMessageBuffer;
int32_t iDataToSend = 0;
const int32_t iSendsBetweenIncrements = 100;
char cTxString[ 12 ]; /* Large enough to hold a 32 number in ASCII. */
const TickType_t xTicksToWait = mbRX_TX_BLOCK_TIME, xShortDelay = pdMS_TO_TICKS( 50 );
StaticMessageBuffer_t xStaticMessageBuffer;
/* The task's priority is used as an index into the loop counters used to
indicate this task is still running. */
UBaseType_t uxIndex = uxTaskPriorityGet( NULL );
/* Make sure a change in priority does not inadvertently result in an
invalid array index. */
configASSERT( uxIndex < mbNUMBER_OF_ECHO_CLIENTS );
/* Avoid compiler warnings about unused parameters. */
( void ) pvParameters;
xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucBufferStorage ) / mbNUMBER_OF_SENDER_TASKS, /* The number of bytes in each buffer in the array. */
&( ucBufferStorage[ uxIndex ][ 0 ] ), /* The address of the buffer to use within the array. */
&( xStaticMessageBuffers[ uxIndex ] ) ); /* The static message buffer structure to use within the array. */
/* Now the message buffer has been created the receiver task can be created.
If this sender task has the higher priority then the receiver task is
created at the lower priority - if this sender task has the lower priority
then the receiver task is created at the higher priority. */
if( uxTaskPriorityGet( NULL ) == mbLOWER_PRIORITY )
{
/* Here prvSingleTaskTests() performs various tests on a message buffer
that was created statically. */
prvSingleTaskTests( xMessageBuffer );
xTaskCreate( prvReceiverTask, "MsgReceiver", xBlockingStackSize, ( void * ) xMessageBuffer, mbHIGHER_PRIORITY, NULL );
}
else
{
xTaskCreate( prvReceiverTask, "MsgReceiver", xBlockingStackSize, ( void * ) xMessageBuffer, mbLOWER_PRIORITY, NULL );
}
for( ;; )
{
/* Create a string from an incrementing number. The length of the
string will increase and decrease as the value of the number increases
then overflows. */
memset( cTxString, 0x00, sizeof( cTxString ) );
sprintf( cTxString, "%d", ( int ) iDataToSend );
xMessageBufferSend( xMessageBuffer, ( void * ) cTxString, strlen( cTxString ), xTicksToWait );
iDataToSend++;
if( ( iDataToSend % iSendsBetweenIncrements ) == 0 )
{
/* Increment a loop counter so a check task can tell this task is
still running as expected. */
ulSenderLoopCounters[ uxIndex ]++;
if( uxTaskPriorityGet( NULL ) == mbHIGHER_PRIORITY )
{
/* Allow other tasks to run. */
vTaskDelay( xShortDelay );
}
/* This message buffer is just created and deleted to ensure no
issues when attempting to delete a message buffer that was
created using statically allocated memory. To save stack space
the buffer is set to point to the cTxString array - this is
ok because nothing is actually written to the memory. */
xTempMessageBuffer = xMessageBufferCreateStatic( sizeof( cTxString ), ( uint8_t * ) cTxString, &xStaticMessageBuffer );
vMessageBufferDelete( xTempMessageBuffer );
}
}
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
static void prvReceiverTask( void *pvParameters )
{
MessageBufferHandle_t * const pxMessageBuffer = ( MessageBufferHandle_t * ) pvParameters;
char cExpectedString[ 12 ]; /* Large enough to hold a 32-bit number in ASCII. */
char cReceivedString[ 12 ]; /* Large enough to hold a 32-bit number in ASCII. */
int32_t iExpectedData = 0;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 5UL );
size_t xReceivedBytes;
for( ;; )
{
/* Generate the next expected string in the cExpectedString buffer. */
memset( cExpectedString, 0x00, sizeof( cExpectedString ) );
sprintf( cExpectedString, "%d", ( int ) iExpectedData );
/* Receive the next string from the message buffer. */
memset( cReceivedString, 0x00, sizeof( cReceivedString ) );
do
{
xReceivedBytes = xMessageBufferReceive( pxMessageBuffer, ( void * ) cReceivedString, sizeof( cExpectedString ), xTicksToWait );
} while( xReceivedBytes == 0 );
/* Ensure the received string matches the expected string. */
configASSERT( strcmp( cExpectedString, cReceivedString ) == 0 );
iExpectedData++;
}
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*-----------------------------------------------------------*/
static void prvEchoClient( void *pvParameters )
{
size_t xSendLength = 0, ux;
char *pcStringToSend, *pcStringReceived, cNextChar = mbASCII_SPACE;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 50 );
/* The task's priority is used as an index into the loop counters used to
indicate this task is still running. */
UBaseType_t uxIndex = uxTaskPriorityGet( NULL );
/* Pointers to the client and server message buffers are passed into this task
using the task's parameter. */
EchoMessageBuffers_t *pxMessageBuffers = ( EchoMessageBuffers_t * ) pvParameters;
/* Prevent compiler warnings. */
( void ) pvParameters;
/* Create the buffer into which strings to send to the server will be
created, and the buffer into which strings echoed back from the server will
be copied. */
pcStringToSend = ( char * ) pvPortMalloc( mbMESSAGE_BUFFER_LENGTH_BYTES );
pcStringReceived = ( char * ) pvPortMalloc( mbMESSAGE_BUFFER_LENGTH_BYTES );
configASSERT( pcStringToSend );
configASSERT( pcStringReceived );
for( ;; )
{
/* Generate the length of the next string to send. */
xSendLength++;
/* The message buffer is being used to hold variable length data, so
each data item requires sizeof( size_t ) bytes to hold the data's
length, hence the sizeof() in the if() condition below. */
if( xSendLength > ( mbMESSAGE_BUFFER_LENGTH_BYTES - sizeof( size_t ) ) )
{
/* Back to a string length of 1. */
xSendLength = sizeof( char );
/* Maintain a count of the number of times this code executes so a
check task can determine if this task is still functioning as
expected or not. As there are two client tasks, and the priorities
used are 0 and 1, the task's priority is used as an index into the
loop count array. */
ulEchoLoopCounters[ uxIndex ]++;
}
memset( pcStringToSend, 0x00, mbMESSAGE_BUFFER_LENGTH_BYTES );
for( ux = 0; ux < xSendLength; ux++ )
{
pcStringToSend[ ux ] = cNextChar;
cNextChar++;
if( cNextChar > mbASCII_TILDA )
{
cNextChar = mbASCII_SPACE;
}
}
/* Send the generated string to the buffer. */
do
{
ux = xMessageBufferSend( pxMessageBuffers->xEchoClientBuffer, ( void * ) pcStringToSend, xSendLength, xTicksToWait );
if( ux == 0 )
{
mtCOVERAGE_TEST_MARKER();
}
} while( ux == 0 );
/* Wait for the string to be echoed back. */
memset( pcStringReceived, 0x00, mbMESSAGE_BUFFER_LENGTH_BYTES );
xMessageBufferReceive( pxMessageBuffers->xEchoServerBuffer, ( void * ) pcStringReceived, xSendLength, portMAX_DELAY );
configASSERT( strcmp( pcStringToSend, pcStringReceived ) == 0 );
}
}
/*-----------------------------------------------------------*/
static void prvEchoServer( void *pvParameters )
{
MessageBufferHandle_t xTempMessageBuffer;
size_t xReceivedLength;
char *pcReceivedString;
EchoMessageBuffers_t xMessageBuffers;
TickType_t xTimeOnEntering;
const TickType_t xTicksToBlock = pdMS_TO_TICKS( 250UL );
/* Prevent compiler warnings about unused parameters. */
( void ) pvParameters;
/* Create the message buffer used to send data from the client to the server,
and the message buffer used to echo the data from the server back to the
client. */
xMessageBuffers.xEchoClientBuffer = xMessageBufferCreate( mbMESSAGE_BUFFER_LENGTH_BYTES );
xMessageBuffers.xEchoServerBuffer = xMessageBufferCreate( mbMESSAGE_BUFFER_LENGTH_BYTES );
configASSERT( xMessageBuffers.xEchoClientBuffer );
configASSERT( xMessageBuffers.xEchoServerBuffer );
/* Create the buffer into which received strings will be copied. */
pcReceivedString = ( char * ) pvPortMalloc( mbMESSAGE_BUFFER_LENGTH_BYTES );
configASSERT( pcReceivedString );
/* Don't expect to receive anything yet! */
xTimeOnEntering = xTaskGetTickCount();
xReceivedLength = xMessageBufferReceive( xMessageBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, mbMESSAGE_BUFFER_LENGTH_BYTES, xTicksToBlock );
configASSERT( ( xTaskGetTickCount() - xTimeOnEntering ) >= xTicksToBlock );
configASSERT( xReceivedLength == 0 );
( void ) xTimeOnEntering; /* In case configASSERT() is not defined. */
/* Now the message buffers have been created the echo client task can be
created. If this server task has the higher priority then the client task
is created at the lower priority - if this server task has the lower
priority then the client task is created at the higher priority. */
if( uxTaskPriorityGet( NULL ) == mbLOWER_PRIORITY )
{
xTaskCreate( prvEchoClient, "EchoClient", configMINIMAL_STACK_SIZE, ( void * ) &xMessageBuffers, mbHIGHER_PRIORITY, NULL );
}
else
{
/* Here prvSingleTaskTests() performs various tests on a message buffer
that was created dynamically. */
prvSingleTaskTests( xMessageBuffers.xEchoClientBuffer );
xTaskCreate( prvEchoClient, "EchoClient", configMINIMAL_STACK_SIZE, ( void * ) &xMessageBuffers, mbLOWER_PRIORITY, NULL );
}
for( ;; )
{
memset( pcReceivedString, 0x00, mbMESSAGE_BUFFER_LENGTH_BYTES );
/* Has any data been sent by the client? */
xReceivedLength = xMessageBufferReceive( xMessageBuffers.xEchoClientBuffer, ( void * ) pcReceivedString, mbMESSAGE_BUFFER_LENGTH_BYTES, portMAX_DELAY );
/* Should always receive data as max delay was used. */
configASSERT( xReceivedLength > 0 );
/* Echo the received data back to the client. */
xMessageBufferSend( xMessageBuffers.xEchoServerBuffer, ( void * ) pcReceivedString, xReceivedLength, portMAX_DELAY );
/* This message buffer is just created and deleted to ensure no memory
leaks. */
xTempMessageBuffer = xMessageBufferCreate( mbMESSAGE_BUFFER_LENGTH_BYTES );
vMessageBufferDelete( xTempMessageBuffer );
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreMessageBufferTasksStillRunning( void )
{
static uint32_t ulLastEchoLoopCounters[ mbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
static uint32_t ulLastNonBlockingRxCounter = 0;
BaseType_t xReturn = pdPASS, x;
for( x = 0; x < mbNUMBER_OF_ECHO_CLIENTS; x++ )
{
if( ulLastEchoLoopCounters[ x ] == ulEchoLoopCounters[ x ] )
{
xReturn = pdFAIL;
}
else
{
ulLastEchoLoopCounters[ x ] = ulEchoLoopCounters[ x ];
}
}
if( ulNonBlockingRxCounter == ulLastNonBlockingRxCounter )
{
xReturn = pdFAIL;
}
else
{
ulLastNonBlockingRxCounter = ulNonBlockingRxCounter;
}
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
static uint32_t ulLastSenderLoopCounters[ mbNUMBER_OF_ECHO_CLIENTS ] = { 0 };
for( x = 0; x < mbNUMBER_OF_SENDER_TASKS; x++ )
{
if( ulLastSenderLoopCounters[ x ] == ulSenderLoopCounters[ x ] )
{
xReturn = pdFAIL;
}
else
{
ulLastSenderLoopCounters[ x ] = ulSenderLoopCounters[ x ];
}
}
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
return xReturn;
}
/*-----------------------------------------------------------*/

View File

@ -0,0 +1,223 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This version of PollQ. c is for use on systems that have limited stack
* space and no display facilities. The complete version can be found in
* the Demo/Common/Full directory.
*
* Creates two tasks that communicate over a single queue. One task acts as a
* producer, the other a consumer.
*
* The producer loops for three iteration, posting an incrementing number onto the
* queue each cycle. It then delays for a fixed period before doing exactly the
* same again.
*
* The consumer loops emptying the queue. Each item removed from the queue is
* checked to ensure it contains the expected value. When the queue is empty it
* blocks for a fixed period, then does the same again.
*
* All queue access is performed without blocking. The consumer completely empties
* the queue each time it runs so the producer should never find the queue full.
*
* An error is flagged if the consumer obtains an unexpected value or the producer
* find the queue is full.
*/
/*
Changes from V2.0.0
+ Delay periods are now specified using variables and constants of
TickType_t rather than uint32_t.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo program include files. */
#include "PollQ.h"
#define pollqSTACK_SIZE configMINIMAL_STACK_SIZE
#define pollqQUEUE_SIZE ( 10 )
#define pollqPRODUCER_DELAY ( pdMS_TO_TICKS( ( TickType_t ) 200 ) )
#define pollqCONSUMER_DELAY ( pollqPRODUCER_DELAY - ( TickType_t ) ( 20 / portTICK_PERIOD_MS ) )
#define pollqNO_DELAY ( ( TickType_t ) 0 )
#define pollqVALUES_TO_PRODUCE ( ( BaseType_t ) 3 )
#define pollqINITIAL_VALUE ( ( BaseType_t ) 0 )
/* The task that posts the incrementing number onto the queue. */
static portTASK_FUNCTION_PROTO( vPolledQueueProducer, pvParameters );
/* The task that empties the queue. */
static portTASK_FUNCTION_PROTO( vPolledQueueConsumer, pvParameters );
/* Variables that are used to check that the tasks are still running with no
errors. */
static volatile BaseType_t xPollingConsumerCount = pollqINITIAL_VALUE, xPollingProducerCount = pollqINITIAL_VALUE;
/*-----------------------------------------------------------*/
void vStartPolledQueueTasks( UBaseType_t uxPriority )
{
static QueueHandle_t xPolledQueue;
/* Create the queue used by the producer and consumer. */
xPolledQueue = xQueueCreate( pollqQUEUE_SIZE, ( UBaseType_t ) sizeof( uint16_t ) );
if( xPolledQueue != NULL )
{
/* vQueueAddToRegistry() adds the queue to the queue registry, if one is
in use. The queue registry is provided as a means for kernel aware
debuggers to locate queues and has no purpose if a kernel aware debugger
is not being used. The call to vQueueAddToRegistry() will be removed
by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
defined to be less than 1. */
vQueueAddToRegistry( xPolledQueue, "Poll_Test_Queue" );
/* Spawn the producer and consumer. */
xTaskCreate( vPolledQueueConsumer, "QConsNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, ( TaskHandle_t * ) NULL );
xTaskCreate( vPolledQueueProducer, "QProdNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, ( TaskHandle_t * ) NULL );
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vPolledQueueProducer, pvParameters )
{
uint16_t usValue = ( uint16_t ) 0;
BaseType_t xError = pdFALSE, xLoop;
for( ;; )
{
for( xLoop = 0; xLoop < pollqVALUES_TO_PRODUCE; xLoop++ )
{
/* Send an incrementing number on the queue without blocking. */
if( xQueueSend( *( ( QueueHandle_t * ) pvParameters ), ( void * ) &usValue, pollqNO_DELAY ) != pdPASS )
{
/* We should never find the queue full so if we get here there
has been an error. */
xError = pdTRUE;
}
else
{
if( xError == pdFALSE )
{
/* If an error has ever been recorded we stop incrementing the
check variable. */
portENTER_CRITICAL();
xPollingProducerCount++;
portEXIT_CRITICAL();
}
/* Update the value we are going to post next time around. */
usValue++;
}
}
/* Wait before we start posting again to ensure the consumer runs and
empties the queue. */
vTaskDelay( pollqPRODUCER_DELAY );
}
} /*lint !e818 Function prototype must conform to API. */
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vPolledQueueConsumer, pvParameters )
{
uint16_t usData, usExpectedValue = ( uint16_t ) 0;
BaseType_t xError = pdFALSE;
for( ;; )
{
/* Loop until the queue is empty. */
while( uxQueueMessagesWaiting( *( ( QueueHandle_t * ) pvParameters ) ) )
{
if( xQueueReceive( *( ( QueueHandle_t * ) pvParameters ), &usData, pollqNO_DELAY ) == pdPASS )
{
if( usData != usExpectedValue )
{
/* This is not what we expected to receive so an error has
occurred. */
xError = pdTRUE;
/* Catch-up to the value we received so our next expected
value should again be correct. */
usExpectedValue = usData;
}
else
{
if( xError == pdFALSE )
{
/* Only increment the check variable if no errors have
occurred. */
portENTER_CRITICAL();
xPollingConsumerCount++;
portEXIT_CRITICAL();
}
}
/* Next time round we would expect the number to be one higher. */
usExpectedValue++;
}
}
/* Now the queue is empty we block, allowing the producer to place more
items in the queue. */
vTaskDelay( pollqCONSUMER_DELAY );
}
} /*lint !e818 Function prototype must conform to API. */
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running with no errors. */
BaseType_t xArePollingQueuesStillRunning( void )
{
BaseType_t xReturn;
/* Check both the consumer and producer poll count to check they have both
been changed since out last trip round. We do not need a critical section
around the check variables as this is called from a higher priority than
the other tasks that access the same variables. */
if( ( xPollingConsumerCount == pollqINITIAL_VALUE ) ||
( xPollingProducerCount == pollqINITIAL_VALUE )
)
{
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
/* Set the check variables back down so we know if they have been
incremented the next time around. */
xPollingConsumerCount = pollqINITIAL_VALUE;
xPollingProducerCount = pollqINITIAL_VALUE;
return xReturn;
}

View File

@ -0,0 +1,439 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Tests the behaviour when data is peeked from a queue when there are
* multiple tasks blocked on the queue.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* Demo program include files. */
#include "QPeek.h"
#define qpeekQUEUE_LENGTH ( 5 )
#define qpeekNO_BLOCK ( 0 )
#define qpeekSHORT_DELAY ( 10 )
#define qpeekLOW_PRIORITY ( tskIDLE_PRIORITY + 0 )
#define qpeekMEDIUM_PRIORITY ( tskIDLE_PRIORITY + 1 )
#define qpeekHIGH_PRIORITY ( tskIDLE_PRIORITY + 2 )
#define qpeekHIGHEST_PRIORITY ( tskIDLE_PRIORITY + 3 )
/*-----------------------------------------------------------*/
/*
* The following three tasks are used to demonstrate the peeking behaviour.
* Each task is given a different priority to demonstrate the order in which
* tasks are woken as data is peeked from a queue.
*/
static void prvLowPriorityPeekTask( void *pvParameters );
static void prvMediumPriorityPeekTask( void *pvParameters );
static void prvHighPriorityPeekTask( void *pvParameters );
static void prvHighestPriorityPeekTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* Flag that will be latched to pdTRUE should any unexpected behaviour be
detected in any of the tasks. */
static volatile BaseType_t xErrorDetected = pdFALSE;
/* Counter that is incremented on each cycle of a test. This is used to
detect a stalled task - a test that is no longer running. */
static volatile uint32_t ulLoopCounter = 0;
/* Handles to the test tasks. */
TaskHandle_t xMediumPriorityTask, xHighPriorityTask, xHighestPriorityTask;
/*-----------------------------------------------------------*/
void vStartQueuePeekTasks( void )
{
QueueHandle_t xQueue;
/* Create the queue that we are going to use for the test/demo. */
xQueue = xQueueCreate( qpeekQUEUE_LENGTH, sizeof( uint32_t ) );
if( xQueue != NULL )
{
/* vQueueAddToRegistry() adds the queue to the queue registry, if one is
in use. The queue registry is provided as a means for kernel aware
debuggers to locate queues and has no purpose if a kernel aware debugger
is not being used. The call to vQueueAddToRegistry() will be removed
by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
defined to be less than 1. */
vQueueAddToRegistry( xQueue, "QPeek_Test_Queue" );
/* Create the demo tasks and pass it the queue just created. We are
passing the queue handle by value so it does not matter that it is declared
on the stack here. */
xTaskCreate( prvLowPriorityPeekTask, "PeekL", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekLOW_PRIORITY, NULL );
xTaskCreate( prvMediumPriorityPeekTask, "PeekM", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekMEDIUM_PRIORITY, &xMediumPriorityTask );
xTaskCreate( prvHighPriorityPeekTask, "PeekH1", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekHIGH_PRIORITY, &xHighPriorityTask );
xTaskCreate( prvHighestPriorityPeekTask, "PeekH2", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekHIGHEST_PRIORITY, &xHighestPriorityTask );
}
}
/*-----------------------------------------------------------*/
static void prvHighestPriorityPeekTask( void *pvParameters )
{
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters;
uint32_t ulValue;
#ifdef USE_STDIO
{
void vPrintDisplayMessage( const char * const * ppcMessageToSend );
const char * const pcTaskStartMsg = "Queue peek test started.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
}
#endif
for( ;; )
{
/* Try peeking from the queue. The queue should be empty so we will
block, allowing the high priority task to execute. */
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS )
{
/* We expected to have received something by the time we unblock. */
xErrorDetected = pdTRUE;
}
/* When we reach here the high and medium priority tasks should still
be blocked on the queue. We unblocked because the low priority task
wrote a value to the queue, which we should have peeked. Peeking the
data (rather than receiving it) will leave the data on the queue, so
the high priority task should then have also been unblocked, but not
yet executed. */
if( ulValue != 0x11223344 )
{
/* We did not receive the expected value. */
xErrorDetected = pdTRUE;
}
if( uxQueueMessagesWaiting( xQueue ) != 1 )
{
/* The message should have been left on the queue. */
xErrorDetected = pdTRUE;
}
/* Now we are going to actually receive the data, so when the high
priority task runs it will find the queue empty and return to the
blocked state. */
ulValue = 0;
if( xQueueReceive( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS )
{
/* We expected to receive the value. */
xErrorDetected = pdTRUE;
}
if( ulValue != 0x11223344 )
{
/* We did not receive the expected value - which should have been
the same value as was peeked. */
xErrorDetected = pdTRUE;
}
/* Now we will block again as the queue is once more empty. The low
priority task can then execute again. */
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS )
{
/* We expected to have received something by the time we unblock. */
xErrorDetected = pdTRUE;
}
/* When we get here the low priority task should have again written to the
queue. */
if( ulValue != 0x01234567 )
{
/* We did not receive the expected value. */
xErrorDetected = pdTRUE;
}
if( uxQueueMessagesWaiting( xQueue ) != 1 )
{
/* The message should have been left on the queue. */
xErrorDetected = pdTRUE;
}
/* We only peeked the data, so suspending ourselves now should enable
the high priority task to also peek the data. The high priority task
will have been unblocked when we peeked the data as we left the data
in the queue. */
vTaskSuspend( NULL );
/* This time we are going to do the same as the above test, but the
high priority task is going to receive the data, rather than peek it.
This means that the medium priority task should never peek the value. */
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( ulValue != 0xaabbaabb )
{
xErrorDetected = pdTRUE;
}
vTaskSuspend( NULL );
}
}
/*-----------------------------------------------------------*/
static void prvHighPriorityPeekTask( void *pvParameters )
{
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters;
uint32_t ulValue;
for( ;; )
{
/* Try peeking from the queue. The queue should be empty so we will
block, allowing the medium priority task to execute. Both the high
and highest priority tasks will then be blocked on the queue. */
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS )
{
/* We expected to have received something by the time we unblock. */
xErrorDetected = pdTRUE;
}
/* When we get here the highest priority task should have peeked the data
(unblocking this task) then suspended (allowing this task to also peek
the data). */
if( ulValue != 0x01234567 )
{
/* We did not receive the expected value. */
xErrorDetected = pdTRUE;
}
if( uxQueueMessagesWaiting( xQueue ) != 1 )
{
/* The message should have been left on the queue. */
xErrorDetected = pdTRUE;
}
/* We only peeked the data, so suspending ourselves now should enable
the medium priority task to also peek the data. The medium priority task
will have been unblocked when we peeked the data as we left the data
in the queue. */
vTaskSuspend( NULL );
/* This time we are going actually receive the value, so the medium
priority task will never peek the data - we removed it from the queue. */
if( xQueueReceive( xQueue, &ulValue, portMAX_DELAY ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( ulValue != 0xaabbaabb )
{
xErrorDetected = pdTRUE;
}
vTaskSuspend( NULL );
}
}
/*-----------------------------------------------------------*/
static void prvMediumPriorityPeekTask( void *pvParameters )
{
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters;
uint32_t ulValue;
for( ;; )
{
/* Try peeking from the queue. The queue should be empty so we will
block, allowing the low priority task to execute. The highest, high
and medium priority tasks will then all be blocked on the queue. */
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS )
{
/* We expected to have received something by the time we unblock. */
xErrorDetected = pdTRUE;
}
/* When we get here the high priority task should have peeked the data
(unblocking this task) then suspended (allowing this task to also peek
the data). */
if( ulValue != 0x01234567 )
{
/* We did not receive the expected value. */
xErrorDetected = pdTRUE;
}
if( uxQueueMessagesWaiting( xQueue ) != 1 )
{
/* The message should have been left on the queue. */
xErrorDetected = pdTRUE;
}
/* Just so we know the test is still running. */
ulLoopCounter++;
/* Now we can suspend ourselves so the low priority task can execute
again. */
vTaskSuspend( NULL );
}
}
/*-----------------------------------------------------------*/
static void prvLowPriorityPeekTask( void *pvParameters )
{
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters;
uint32_t ulValue;
for( ;; )
{
/* Write some data to the queue. This should unblock the highest
priority task that is waiting to peek data from the queue. */
ulValue = 0x11223344;
if( xQueueSendToBack( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS )
{
/* We were expecting the queue to be empty so we should not of
had a problem writing to the queue. */
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* By the time we get here the data should have been removed from
the queue. */
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
xErrorDetected = pdTRUE;
}
/* Write another value to the queue, again waking the highest priority
task that is blocked on the queue. */
ulValue = 0x01234567;
if( xQueueSendToBack( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS )
{
/* We were expecting the queue to be empty so we should not of
had a problem writing to the queue. */
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* All the other tasks should now have successfully peeked the data.
The data is still in the queue so we should be able to receive it. */
ulValue = 0;
if( xQueueReceive( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS )
{
/* We expected to receive the data. */
xErrorDetected = pdTRUE;
}
if( ulValue != 0x01234567 )
{
/* We did not receive the expected value. */
}
/* Lets just delay a while as this is an intensive test as we don't
want to starve other tests of processing time. */
vTaskDelay( qpeekSHORT_DELAY );
/* Unsuspend the other tasks so we can repeat the test - this time
however not all the other tasks will peek the data as the high
priority task is actually going to remove it from the queue. Send
to front is used just to be different. As the queue is empty it
makes no difference to the result. */
vTaskResume( xMediumPriorityTask );
vTaskResume( xHighPriorityTask );
vTaskResume( xHighestPriorityTask );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
ulValue = 0xaabbaabb;
if( xQueueSendToFront( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS )
{
/* We were expecting the queue to be empty so we should not of
had a problem writing to the queue. */
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* This time we should find that the queue is empty. The high priority
task actually removed the data rather than just peeking it. */
if( xQueuePeek( xQueue, &ulValue, qpeekNO_BLOCK ) != errQUEUE_EMPTY )
{
/* We expected to receive the data. */
xErrorDetected = pdTRUE;
}
/* Unsuspend the highest and high priority tasks so we can go back
and repeat the whole thing. The medium priority task should not be
suspended as it was not able to peek the data in this last case. */
vTaskResume( xHighPriorityTask );
vTaskResume( xHighestPriorityTask );
/* Lets just delay a while as this is an intensive test as we don't
want to starve other tests of processing time. */
vTaskDelay( qpeekSHORT_DELAY );
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreQueuePeekTasksStillRunning( void )
{
static uint32_t ulLastLoopCounter = 0;
/* If the demo task is still running then we expect the loopcounter to
have incremented since this function was last called. */
if( ulLastLoopCounter == ulLoopCounter )
{
xErrorDetected = pdTRUE;
}
ulLastLoopCounter = ulLoopCounter;
/* Errors detected in the task itself will have latched xErrorDetected
to true. */
return ( BaseType_t ) !xErrorDetected;
}

View File

@ -0,0 +1,230 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Basic task to demonstrate the xQueueOverwrite() function. See the comments
* in the function itself.
*/
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo program include files. */
#include "QueueOverwrite.h"
/* A block time of 0 just means "don't block". */
#define qoDONT_BLOCK 0
/* Number of times to overwrite the value in the queue. */
#define qoLOOPS 5
/* The task that uses the queue. */
static void prvQueueOverwriteTask( void *pvParameters );
/* Variable that is incremented on each loop of prvQueueOverwriteTask() provided
prvQueueOverwriteTask() has not found any errors. */
static uint32_t ulLoopCounter = 0;
/* Set to pdFALSE if an error is discovered by the
vQueueOverwritePeriodicISRDemo() function. */
static BaseType_t xISRTestStatus = pdPASS;
/* The queue that is accessed from the ISR. The queue accessed by the task is
created inside the task itself. */
static QueueHandle_t xISRQueue = NULL;
/*-----------------------------------------------------------*/
void vStartQueueOverwriteTask( UBaseType_t uxPriority )
{
const UBaseType_t uxQueueLength = 1;
/* Create the queue used by the ISR. xQueueOverwriteFromISR() should only
be used on queues that have a length of 1. */
xISRQueue = xQueueCreate( uxQueueLength, ( UBaseType_t ) sizeof( uint32_t ) );
/* Create the test task. The queue used by the test task is created inside
the task itself. */
xTaskCreate( prvQueueOverwriteTask, "QOver", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL );
}
/*-----------------------------------------------------------*/
static void prvQueueOverwriteTask( void *pvParameters )
{
QueueHandle_t xTaskQueue;
const UBaseType_t uxQueueLength = 1;
uint32_t ulValue, ulStatus = pdPASS, x;
/* The parameter is not used. */
( void ) pvParameters;
/* Create the queue. xQueueOverwrite() should only be used on queues that
have a length of 1. */
xTaskQueue = xQueueCreate( uxQueueLength, ( UBaseType_t ) sizeof( uint32_t ) );
configASSERT( xTaskQueue );
for( ;; )
{
/* The queue is empty. Writing to the queue then reading from the queue
should return the item written. */
ulValue = 10;
xQueueOverwrite( xTaskQueue, &ulValue );
ulValue = 0;
xQueueReceive( xTaskQueue, &ulValue, qoDONT_BLOCK );
if( ulValue != 10 )
{
ulStatus = pdFAIL;
}
/* Now try writing to the queue several times. Each time the value
in the queue should get overwritten. */
for( x = 0; x < qoLOOPS; x++ )
{
/* Write to the queue. */
xQueueOverwrite( xTaskQueue, &x );
/* Check the value in the queue is that written, even though the
queue was not necessarily empty. */
xQueuePeek( xTaskQueue, &ulValue, qoDONT_BLOCK );
if( ulValue != x )
{
ulStatus = pdFAIL;
}
/* There should always be one item in the queue. */
if( uxQueueMessagesWaiting( xTaskQueue ) != uxQueueLength )
{
ulStatus = pdFAIL;
}
}
/* Empty the queue again. */
xQueueReceive( xTaskQueue, &ulValue, qoDONT_BLOCK );
if( uxQueueMessagesWaiting( xTaskQueue ) != 0 )
{
ulStatus = pdFAIL;
}
if( ulStatus != pdFAIL )
{
/* Increment a counter to show this task is still running without
error. */
ulLoopCounter++;
}
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
BaseType_t xIsQueueOverwriteTaskStillRunning( void )
{
BaseType_t xReturn;
if( xISRTestStatus != pdPASS )
{
xReturn = pdFAIL;
}
else if( ulLoopCounter > 0 )
{
xReturn = pdPASS;
}
else
{
/* The task has either stalled of discovered an error. */
xReturn = pdFAIL;
}
ulLoopCounter = 0;
return xReturn;
}
/*-----------------------------------------------------------*/
void vQueueOverwritePeriodicISRDemo( void )
{
static uint32_t ulCallCount = 0;
const uint32_t ulTx1 = 10UL, ulTx2 = 20UL, ulNumberOfSwitchCases = 3UL;
uint32_t ulRx;
/* This function should be called from an interrupt, such as the tick hook
function vApplicationTickHook(). */
configASSERT( xISRQueue );
switch( ulCallCount )
{
case 0:
/* The queue is empty. Write ulTx1 to the queue. In this demo the
last parameter is not used because there are no tasks blocked on
this queue. */
xQueueOverwriteFromISR( xISRQueue, &ulTx1, NULL );
/* Peek the queue to check it holds the expected value. */
xQueuePeekFromISR( xISRQueue, &ulRx );
if( ulRx != ulTx1 )
{
xISRTestStatus = pdFAIL;
}
break;
case 1:
/* The queue already holds ulTx1. Overwrite the value in the queue
with ulTx2. */
xQueueOverwriteFromISR( xISRQueue, &ulTx2, NULL );
break;
case 2:
/* Read from the queue to empty the queue again. The value read
should be ulTx2. */
xQueueReceiveFromISR( xISRQueue, &ulRx, NULL );
if( ulRx != ulTx2 )
{
xISRTestStatus = pdFAIL;
}
break;
}
/* Run the next case in the switch statement above next time this function
is called. */
ulCallCount++;
if( ulCallCount >= ulNumberOfSwitchCases )
{
/* Go back to the start. */
ulCallCount = 0;
}
}

View File

@ -0,0 +1,775 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Tests the use of queue sets.
*
* A receive task creates a number of queues and adds them to a queue set before
* blocking on the queue set receive. A transmit task and (optionally) an
* interrupt repeatedly unblocks the receive task by sending messages to the
* queues in a pseudo random order. The receive task removes the messages from
* the queues and flags an error if the received message does not match that
* expected. The task sends values in the range 0 to
* queuesetINITIAL_ISR_TX_VALUE, and the ISR sends value in the range
* queuesetINITIAL_ISR_TX_VALUE to ULONG_MAX.
*/
/* Standard includes. */
#include <stdlib.h>
#include <limits.h>
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo includes. */
#include "QueueSet.h"
/* The number of queues that are created and added to the queue set. */
#define queuesetNUM_QUEUES_IN_SET 3
/* The length of each created queue. */
#define queuesetQUEUE_LENGTH 3
/* Block times used in this demo. A block time or 0 means "don't block". */
#define queuesetSHORT_DELAY 200
#define queuesetDONT_BLOCK 0
/* Messages are sent in incrementing order from both a task and an interrupt.
The task sends values in the range 0 to 0xfffe, and the interrupt sends values
in the range of 0xffff to ULONG_MAX. */
#define queuesetINITIAL_ISR_TX_VALUE 0xffffUL
/* The priorities used in this demo. */
#define queuesetLOW_PRIORITY ( tskIDLE_PRIORITY )
#define queuesetMEDIUM_PRIORITY ( queuesetLOW_PRIORITY + 1 )
/* For test purposes the priority of the sending task is changed after every
queuesetPRIORITY_CHANGE_LOOPS number of values are sent to a queue. */
#define queuesetPRIORITY_CHANGE_LOOPS ( ( queuesetNUM_QUEUES_IN_SET * queuesetQUEUE_LENGTH ) * 2 )
/* The ISR sends to the queue every queuesetISR_TX_PERIOD ticks. */
#define queuesetISR_TX_PERIOD ( 100UL )
/* A delay inserted when the Tx task changes its priority to be above the idle
task priority to ensure the idle priority tasks get some CPU time before the
next iteration of the queue set Tx task. */
#define queuesetTX_LOOP_DELAY pdMS_TO_TICKS( ( TickType_t ) 200 )
/* The allowable maximum deviation between a received value and the expected
received value. A deviation will occur when data is received from a queue
inside an ISR in between a task receiving from a queue and the task checking
the received value. */
#define queuesetALLOWABLE_RX_DEVIATION 3
/* Ignore values that are at the boundaries of allowable values to make the
testing of limits easier (don't have to deal with wrapping values). */
#define queuesetIGNORED_BOUNDARY ( queuesetALLOWABLE_RX_DEVIATION * 2 )
typedef enum
{
eEqualPriority = 0, /* Tx and Rx tasks have the same priority. */
eTxHigherPriority, /* The priority of the Tx task is above that of the Rx task. */
eTxLowerPriority /* The priority of the Tx task is below that of the Rx task. */
} eRelativePriorities;
/*
* The task that periodically sends to the queue set.
*/
static void prvQueueSetSendingTask( void *pvParameters );
/*
* The task that reads from the queue set.
*/
static void prvQueueSetReceivingTask( void *pvParameters );
/*
* Check the value received from a queue is the expected value. Some values
* originate from the send task, some values originate from the ISR, with the
* range of the value being used to distinguish between the two message
* sources.
*/
static void prvCheckReceivedValue( uint32_t ulReceived );
/*
* For purposes of test coverage, functions that read from and write to a
* queue set from an ISR respectively.
*/
static void prvReceiveFromQueueInSetFromISR( void );
static void prvSendToQueueInSetFromISR( void );
/*
* Create the queues and add them to a queue set before resuming the Tx
* task.
*/
static void prvSetupTest( void );
/*
* Checks a value received from a queue falls within the range of expected
* values.
*/
static BaseType_t prvCheckReceivedValueWithinExpectedRange( uint32_t ulReceived, uint32_t ulExpectedReceived );
/*
* Increase test coverage by occasionally change the priorities of the two tasks
* relative to each other.
*/
static void prvChangeRelativePriorities( void );
/*
* Queue overwrites can only be performed on queues of length of one, requiring
* a special test function so a queue of length 1 can temporarily be added to a
* set.
*/
static void prvTestQueueOverwriteWithQueueSet( void );
/*
* Local pseudo random number seed and return functions. Used to avoid calls
* to the standard library.
*/
static size_t prvRand( void );
static void prvSRand( size_t uxSeed );
/*-----------------------------------------------------------*/
/* The queues that are added to the set. */
static QueueHandle_t xQueues[ queuesetNUM_QUEUES_IN_SET ] = { 0 };
/* Counts how many times each queue in the set is used to ensure all the
queues are used. */
static uint32_t ulQueueUsedCounter[ queuesetNUM_QUEUES_IN_SET ] = { 0 };
/* The handle of the queue set to which the queues are added. */
static QueueSetHandle_t xQueueSet;
/* If the prvQueueSetReceivingTask() task has not detected any errors then
it increments ulCycleCounter on each iteration.
xAreQueueSetTasksStillRunning() returns pdPASS if the value of
ulCycleCounter has changed between consecutive calls, and pdFALSE if
ulCycleCounter has stopped incrementing (indicating an error condition). */
static volatile uint32_t ulCycleCounter = 0UL;
/* Set to pdFAIL if an error is detected by any queue set task.
ulCycleCounter will only be incremented if xQueueSetTasksSatus equals pdPASS. */
static volatile BaseType_t xQueueSetTasksStatus = pdPASS;
/* Just a flag to let the function that writes to a queue from an ISR know that
the queues are setup and can be used. */
static volatile BaseType_t xSetupComplete = pdFALSE;
/* The value sent to the queue from the ISR is file scope so the
xAreQueeuSetTasksStillRunning() function can check it is incrementing as
expected. */
static volatile uint32_t ulISRTxValue = queuesetINITIAL_ISR_TX_VALUE;
/* Used by the pseudo random number generator. */
static size_t uxNextRand = 0;
/* The task handles are stored so their priorities can be changed. */
TaskHandle_t xQueueSetSendingTask, xQueueSetReceivingTask;
/*-----------------------------------------------------------*/
void vStartQueueSetTasks( void )
{
/* Create the tasks. */
xTaskCreate( prvQueueSetSendingTask, "SetTx", configMINIMAL_STACK_SIZE, NULL, queuesetMEDIUM_PRIORITY, &xQueueSetSendingTask );
if( xQueueSetSendingTask != NULL )
{
xTaskCreate( prvQueueSetReceivingTask, "SetRx", configMINIMAL_STACK_SIZE, ( void * ) xQueueSetSendingTask, queuesetMEDIUM_PRIORITY, &xQueueSetReceivingTask );
/* It is important that the sending task does not attempt to write to a
queue before the queue has been created. It is therefore placed into
the suspended state before the scheduler has started. It is resumed by
the receiving task after the receiving task has created the queues and
added the queues to the queue set. */
vTaskSuspend( xQueueSetSendingTask );
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreQueueSetTasksStillRunning( void )
{
static uint32_t ulLastCycleCounter, ulLastISRTxValue = 0;
static uint32_t ulLastQueueUsedCounter[ queuesetNUM_QUEUES_IN_SET ] = { 0 };
BaseType_t xReturn = pdPASS, x;
if( ulLastCycleCounter == ulCycleCounter )
{
/* The cycle counter is no longer being incremented. Either one of the
tasks is stalled or an error has been detected. */
xReturn = pdFAIL;
}
ulLastCycleCounter = ulCycleCounter;
/* Ensure that all the queues in the set have been used. This ensures the
test is working as intended and guards against the rand() in the Tx task
missing some values. */
for( x = 0; x < queuesetNUM_QUEUES_IN_SET; x++ )
{
if( ulLastQueueUsedCounter[ x ] == ulQueueUsedCounter[ x ] )
{
xReturn = pdFAIL;
}
ulLastQueueUsedCounter[ x ] = ulQueueUsedCounter[ x ];
}
/* Check the global status flag. */
if( xQueueSetTasksStatus != pdPASS )
{
xReturn = pdFAIL;
}
/* Check that the ISR is still sending values to the queues too. */
if( ulISRTxValue == ulLastISRTxValue )
{
xReturn = pdFAIL;
}
else
{
ulLastISRTxValue = ulISRTxValue;
}
return xReturn;
}
/*-----------------------------------------------------------*/
static void prvQueueSetSendingTask( void *pvParameters )
{
uint32_t ulTaskTxValue = 0;
size_t uxQueueToWriteTo;
QueueHandle_t xQueueInUse;
/* Remove compiler warning about the unused parameter. */
( void ) pvParameters;
/* Seed mini pseudo random number generator. */
prvSRand( ( size_t ) &ulTaskTxValue );
for( ;; )
{
/* Generate the index for the queue to which a value is to be sent. */
uxQueueToWriteTo = prvRand() % queuesetNUM_QUEUES_IN_SET;
xQueueInUse = xQueues[ uxQueueToWriteTo ];
/* Note which index is being written to to ensure all the queues are
used. */
( ulQueueUsedCounter[ uxQueueToWriteTo ] )++;
/* Send to the queue to unblock the task that is waiting for data to
arrive on a queue within the queue set to which this queue belongs. */
if( xQueueSendToBack( xQueueInUse, &ulTaskTxValue, portMAX_DELAY ) != pdPASS )
{
/* The send should always pass as an infinite block time was
used. */
xQueueSetTasksStatus = pdFAIL;
}
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
ulTaskTxValue++;
/* If the Tx value has reached the range used by the ISR then set it
back to 0. */
if( ulTaskTxValue == queuesetINITIAL_ISR_TX_VALUE )
{
ulTaskTxValue = 0;
}
/* Increase test coverage by occasionally change the priorities of the
two tasks relative to each other. */
prvChangeRelativePriorities();
}
}
/*-----------------------------------------------------------*/
static void prvChangeRelativePriorities( void )
{
static UBaseType_t ulLoops = 0;
static eRelativePriorities ePriorities = eEqualPriority;
/* Occasionally change the task priority relative to the priority of
the receiving task. */
ulLoops++;
if( ulLoops >= queuesetPRIORITY_CHANGE_LOOPS )
{
ulLoops = 0;
switch( ePriorities )
{
case eEqualPriority:
/* Both tasks are running with medium priority. Now lower the
priority of the receiving task so the Tx task has the higher
relative priority. */
vTaskPrioritySet( xQueueSetReceivingTask, queuesetLOW_PRIORITY );
ePriorities = eTxHigherPriority;
break;
case eTxHigherPriority:
/* The Tx task is running with a higher priority than the Rx
task. Switch the priorities around so the Rx task has the
higher relative priority. */
vTaskPrioritySet( xQueueSetReceivingTask, queuesetMEDIUM_PRIORITY );
vTaskPrioritySet( xQueueSetSendingTask, queuesetLOW_PRIORITY );
ePriorities = eTxLowerPriority;
break;
case eTxLowerPriority:
/* The Tx task is running with a lower priority than the Rx
task. Make the priorities equal again. */
vTaskPrioritySet( xQueueSetSendingTask, queuesetMEDIUM_PRIORITY );
ePriorities = eEqualPriority;
/* When both tasks are using a non-idle priority the queue set
tasks will starve idle priority tasks of execution time - so
relax a bit before the next iteration to minimise the impact. */
vTaskDelay( queuesetTX_LOOP_DELAY );
break;
}
}
}
/*-----------------------------------------------------------*/
static void prvQueueSetReceivingTask( void *pvParameters )
{
uint32_t ulReceived;
QueueHandle_t xActivatedQueue;
TickType_t xBlockTime;
/* Remove compiler warnings. */
( void ) pvParameters;
/* Create the queues and add them to the queue set before resuming the Tx
task. */
prvSetupTest();
for( ;; )
{
/* For test coverage reasons, the block time is dependent on the
priority of this task - which changes during the test. When the task
is at the idle priority it polls the queue set. */
if( uxTaskPriorityGet( NULL ) == tskIDLE_PRIORITY )
{
xBlockTime = 0;
}
else
{
xBlockTime = portMAX_DELAY;
}
/* Wait for a message to arrive on one of the queues in the set. */
xActivatedQueue = xQueueSelectFromSet( xQueueSet, portMAX_DELAY );
if( xActivatedQueue == NULL )
{
if( xBlockTime != 0 )
{
/* This should not happen as an infinite delay was used. */
xQueueSetTasksStatus = pdFAIL;
}
}
else
{
/* Reading from the queue should pass with a zero block time as
this task will only run when something has been posted to a task
in the queue set. */
if( xQueueReceive( xActivatedQueue, &ulReceived, queuesetDONT_BLOCK ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Ensure the value received was the value expected. This function
manipulates file scope data and is also called from an ISR, hence
the critical section. */
taskENTER_CRITICAL();
{
prvCheckReceivedValue( ulReceived );
}
taskEXIT_CRITICAL();
if( xQueueSetTasksStatus == pdPASS )
{
ulCycleCounter++;
}
}
}
}
/*-----------------------------------------------------------*/
void vQueueSetAccessQueueSetFromISR( void )
{
static uint32_t ulCallCount = 0;
/* xSetupComplete is set to pdTRUE when the queues have been created and
are available for use. */
if( xSetupComplete == pdTRUE )
{
/* It is intended that this function is called from the tick hook
function, so each call is one tick period apart. */
ulCallCount++;
if( ulCallCount > queuesetISR_TX_PERIOD )
{
ulCallCount = 0;
/* First attempt to read from the queue set. */
prvReceiveFromQueueInSetFromISR();
/* Then write to the queue set. */
prvSendToQueueInSetFromISR();
}
}
}
/*-----------------------------------------------------------*/
static void prvCheckReceivedValue( uint32_t ulReceived )
{
static uint32_t ulExpectedReceivedFromTask = 0, ulExpectedReceivedFromISR = queuesetINITIAL_ISR_TX_VALUE;
/* Values are received in tasks and interrupts. It is likely that the
receiving task will sometimes get preempted by the receiving interrupt
between reading a value from the queue and calling this function. When
that happens, if the receiving interrupt calls this function the values
will get passed into this function slightly out of order. For that
reason the value passed in is tested against a small range of expected
values, rather than a single absolute value. To make the range testing
easier values in the range limits are ignored. */
/* If the received value is equal to or greater than
queuesetINITIAL_ISR_TX_VALUE then it was sent by an ISR. */
if( ulReceived >= queuesetINITIAL_ISR_TX_VALUE )
{
/* The value was sent from the ISR. */
if( ( ulReceived - queuesetINITIAL_ISR_TX_VALUE ) < queuesetIGNORED_BOUNDARY )
{
/* The value received is at the lower limit of the expected range.
Don't test it and expect to receive one higher next time. */
}
else if( ( ULONG_MAX - ulReceived ) <= queuesetIGNORED_BOUNDARY )
{
/* The value received is at the higher limit of the expected range.
Don't test it and expect to wrap soon. */
}
else
{
/* Check the value against its expected value range. */
if( prvCheckReceivedValueWithinExpectedRange( ulReceived, ulExpectedReceivedFromISR ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
}
configASSERT( xQueueSetTasksStatus );
/* It is expected to receive an incrementing number. */
ulExpectedReceivedFromISR++;
if( ulExpectedReceivedFromISR == 0 )
{
ulExpectedReceivedFromISR = queuesetINITIAL_ISR_TX_VALUE;
}
}
else
{
/* The value was sent from the Tx task. */
if( ulReceived < queuesetIGNORED_BOUNDARY )
{
/* The value received is at the lower limit of the expected range.
Don't test it, and expect to receive one higher next time. */
}
else if( ( ( queuesetINITIAL_ISR_TX_VALUE - 1 ) - ulReceived ) <= queuesetIGNORED_BOUNDARY )
{
/* The value received is at the higher limit of the expected range.
Don't test it and expect to wrap soon. */
}
else
{
/* Check the value against its expected value range. */
if( prvCheckReceivedValueWithinExpectedRange( ulReceived, ulExpectedReceivedFromTask ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
}
configASSERT( xQueueSetTasksStatus );
/* It is expected to receive an incrementing number. */
ulExpectedReceivedFromTask++;
if( ulExpectedReceivedFromTask >= queuesetINITIAL_ISR_TX_VALUE )
{
ulExpectedReceivedFromTask = 0;
}
}
}
/*-----------------------------------------------------------*/
static BaseType_t prvCheckReceivedValueWithinExpectedRange( uint32_t ulReceived, uint32_t ulExpectedReceived )
{
BaseType_t xReturn = pdPASS;
if( ulReceived > ulExpectedReceived )
{
configASSERT( ( ulReceived - ulExpectedReceived ) <= queuesetALLOWABLE_RX_DEVIATION );
if( ( ulReceived - ulExpectedReceived ) > queuesetALLOWABLE_RX_DEVIATION )
{
xReturn = pdFALSE;
}
}
else
{
configASSERT( ( ulExpectedReceived - ulReceived ) <= queuesetALLOWABLE_RX_DEVIATION );
if( ( ulExpectedReceived - ulReceived ) > queuesetALLOWABLE_RX_DEVIATION )
{
xReturn = pdFALSE;
}
}
return xReturn;
}
/*-----------------------------------------------------------*/
static void prvReceiveFromQueueInSetFromISR( void )
{
QueueSetMemberHandle_t xActivatedQueue;
uint32_t ulReceived;
/* See if any of the queues in the set contain data. */
xActivatedQueue = xQueueSelectFromSetFromISR( xQueueSet );
if( xActivatedQueue != NULL )
{
/* Reading from the queue for test purposes only. */
if( xQueueReceiveFromISR( xActivatedQueue, &ulReceived, NULL ) != pdPASS )
{
/* Data should have been available as the handle was returned from
xQueueSelectFromSetFromISR(). */
xQueueSetTasksStatus = pdFAIL;
}
/* Ensure the value received was the value expected. */
prvCheckReceivedValue( ulReceived );
}
}
/*-----------------------------------------------------------*/
static void prvSendToQueueInSetFromISR( void )
{
static BaseType_t xQueueToWriteTo = 0;
uint32_t ulTxValueSnapshot = ulISRTxValue;
if( xQueueSendFromISR( xQueues[ xQueueToWriteTo ], ( void * ) &ulTxValueSnapshot, NULL ) == pdPASS )
{
ulISRTxValue++;
/* If the Tx value has wrapped then set it back to its initial value. */
if( ulISRTxValue == 0UL )
{
ulISRTxValue = queuesetINITIAL_ISR_TX_VALUE;
}
/* Use a different queue next time. */
xQueueToWriteTo++;
if( xQueueToWriteTo >= queuesetNUM_QUEUES_IN_SET )
{
xQueueToWriteTo = 0;
}
}
}
/*-----------------------------------------------------------*/
static void prvTestQueueOverwriteWithQueueSet( void )
{
uint32_t ulValueToSend = 0, ulValueReceived = 0;
QueueHandle_t xQueueHandle = NULL, xReceivedHandle = NULL;
const UBaseType_t xLengthOfOne = ( UBaseType_t ) 1;
/* Create a queue that has a length of one - a requirement in order to call
xQueueOverwrite. This will get deleted again when this test completes. */
xQueueHandle = xQueueCreate( xLengthOfOne, sizeof( uint32_t ) );
if( xQueueHandle != NULL )
{
xQueueAddToSet( xQueueHandle, xQueueSet );
/* Add an item to the queue then ensure the queue set correctly
indicates that one item is available, and that that item is indeed the
queue written to. */
xQueueOverwrite( xQueueHandle, ( void * ) &ulValueToSend );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* Expected one item in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueuePeek( xQueueSet, &xReceivedHandle, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Now overwrite the value in the queue and ensure the queue set state
doesn't change as the number of items in the queues within the set have
not changed. */
ulValueToSend++;
xQueueOverwrite( xQueueHandle, ( void * ) &ulValueToSend );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* Still expected one item in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Also ensure the value received from the queue is the overwritten
value, not the value originally written. */
xQueueReceive( xQueueHandle, &ulValueReceived, queuesetDONT_BLOCK );
if( ulValueReceived != ulValueToSend )
{
/* Unexpected value recevied from the queue. */
xQueueSetTasksStatus = pdFAIL;
}
/* Clean up. */
xQueueRemoveFromSet( xQueueHandle, xQueueSet );
vQueueDelete( xQueueHandle );
}
}
/*-----------------------------------------------------------*/
static void prvSetupTest( void )
{
BaseType_t x;
uint32_t ulValueToSend = 0;
/* Ensure the queues are created and the queue set configured before the
sending task is unsuspended.
First Create the queue set such that it will be able to hold a message for
every space in every queue in the set. */
xQueueSet = xQueueCreateSet( queuesetNUM_QUEUES_IN_SET * queuesetQUEUE_LENGTH );
for( x = 0; x < queuesetNUM_QUEUES_IN_SET; x++ )
{
/* Create the queue and add it to the set. The queue is just holding
uint32_t value. */
xQueues[ x ] = xQueueCreate( queuesetQUEUE_LENGTH, sizeof( uint32_t ) );
configASSERT( xQueues[ x ] );
if( xQueueAddToSet( xQueues[ x ], xQueueSet ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
else
{
/* The queue has now been added to the queue set and cannot be added to
another. */
if( xQueueAddToSet( xQueues[ x ], xQueueSet ) != pdFAIL )
{
xQueueSetTasksStatus = pdFAIL;
}
}
}
/* Attempt to remove a queue from a queue set it does not belong
to (NULL being passed as the queue set in this case). */
if( xQueueRemoveFromSet( xQueues[ 0 ], NULL ) != pdFAIL )
{
/* It is not possible to successfully remove a queue from a queue
set it does not belong to. */
xQueueSetTasksStatus = pdFAIL;
}
/* Attempt to remove a queue from the queue set it does belong to. */
if( xQueueRemoveFromSet( xQueues[ 0 ], xQueueSet ) != pdPASS )
{
/* It should be possible to remove the queue from the queue set it
does belong to. */
xQueueSetTasksStatus = pdFAIL;
}
/* Add an item to the queue before attempting to add it back into the
set. */
xQueueSend( xQueues[ 0 ], ( void * ) &ulValueToSend, 0 );
if( xQueueAddToSet( xQueues[ 0 ], xQueueSet ) != pdFAIL )
{
/* Should not be able to add a non-empty queue to a set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Remove the item from the queue before adding the queue back into the
set so the dynamic tests can begin. */
xQueueReceive( xQueues[ 0 ], &ulValueToSend, 0 );
if( xQueueAddToSet( xQueues[ 0 ], xQueueSet ) != pdPASS )
{
/* If the queue was successfully removed from the queue set then it
should be possible to add it back in again. */
xQueueSetTasksStatus = pdFAIL;
}
/* The task that sends to the queues is not running yet, so attempting to
read from the queue set should fail. */
if( xQueueSelectFromSet( xQueueSet, queuesetSHORT_DELAY ) != NULL )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Testing the behaviour of queue sets when a queue overwrite operation is
performed on a set member requires a special test as overwrites can only
be performed on queues that have a length of 1. */
prvTestQueueOverwriteWithQueueSet();
/* Resume the task that writes to the queues. */
vTaskResume( xQueueSetSendingTask );
/* Let the ISR access the queues also. */
xSetupComplete = pdTRUE;
}
/*-----------------------------------------------------------*/
static size_t prvRand( void )
{
uxNextRand = ( uxNextRand * ( size_t ) 1103515245 ) + ( size_t ) 12345;
return ( uxNextRand / ( size_t ) 65536 ) % ( size_t ) 32768;
}
/*-----------------------------------------------------------*/
static void prvSRand( size_t uxSeed )
{
uxNextRand = uxSeed;
}

View File

@ -0,0 +1,179 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Tests the use of queue sets.
*
* A receive task creates a number of queues and adds them to a queue set before
* blocking on the queue set receive. A transmit task and (optionally) an
* interrupt repeatedly unblocks the receive task by sending messages to the
* queues in a pseudo random order. The receive task removes the messages from
* the queues and flags an error if the received message does not match that
* expected. The task sends values in the range 0 to
* queuesetINITIAL_ISR_TX_VALUE, and the ISR sends value in the range
* queuesetINITIAL_ISR_TX_VALUE to ULONG_MAX.
*/
/* Standard includes. */
#include <stdlib.h>
#include <limits.h>
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo includes. */
#include "QueueSetPolling.h"
/* The length of each created queue. */
#define setpollQUEUE_LENGTH 10
/* Block times used in this demo. A block time or 0 means "don't block". */
#define setpollDONT_BLOCK 0
/* The ISR sends to the queue every setpollISR_TX_PERIOD ticks. */
#define queuesetISR_TX_PERIOD ( 50UL )
/*
* The task that reads from the queue set.
*/
static void prvQueueSetReceivingTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* The queue that is added to the set. */
static QueueHandle_t xQueue = NULL;
/* The handle of the queue set to which the queue is added. */
static QueueSetHandle_t xQueueSet = NULL;
/* Set to pdFAIL if an error is detected by any queue set task.
ulCycleCounter will only be incremented if xQueueSetTasksSatus equals pdPASS. */
static volatile BaseType_t xQueueSetPollStatus = pdPASS;
/* Counter used to ensure the task is still running. */
static uint32_t ulCycleCounter = 0;
/*-----------------------------------------------------------*/
void vStartQueueSetPollingTask( void )
{
/* Create the queue that is added to the set, the set, and add the queue to
the set. */
xQueue = xQueueCreate( setpollQUEUE_LENGTH, sizeof( uint32_t ) );
xQueueSet = xQueueCreateSet( setpollQUEUE_LENGTH );
if( ( xQueue != NULL ) && ( xQueueSet != NULL ) )
{
xQueueAddToSet( xQueue, xQueueSet );
/* Create the task. */
xTaskCreate( prvQueueSetReceivingTask, "SetPoll", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
}
}
/*-----------------------------------------------------------*/
static void prvQueueSetReceivingTask( void *pvParameters )
{
uint32_t ulReceived, ulExpected = 0;
QueueHandle_t xActivatedQueue;
/* Remove compiler warnings. */
( void ) pvParameters;
for( ;; )
{
/* Is a message waiting? A block time is not used to ensure the queue
set is polled while it is being written to from an interrupt. */
xActivatedQueue = xQueueSelectFromSet( xQueueSet, setpollDONT_BLOCK );
if( xActivatedQueue != NULL )
{
/* Reading from the queue should pass with a zero block time as
this task will only run when something has been posted to a task
in the queue set. */
if( xQueueReceive( xActivatedQueue, &ulReceived, setpollDONT_BLOCK ) != pdPASS )
{
xQueueSetPollStatus = pdFAIL;
}
if( ulReceived == ulExpected )
{
ulExpected++;
}
else
{
xQueueSetPollStatus = pdFAIL;
}
if( xQueueSetPollStatus == pdPASS )
{
ulCycleCounter++;
}
}
}
}
/*-----------------------------------------------------------*/
void vQueueSetPollingInterruptAccess( void )
{
static uint32_t ulCallCount = 0, ulValueToSend = 0;
/* It is intended that this function is called from the tick hook
function, so each call is one tick period apart. */
ulCallCount++;
if( ulCallCount > queuesetISR_TX_PERIOD )
{
ulCallCount = 0;
if( xQueueSendFromISR( xQueue, ( void * ) &ulValueToSend, NULL ) == pdPASS )
{
/* Send the next value next time. */
ulValueToSend++;
}
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreQueueSetPollTasksStillRunning( void )
{
static uint32_t ulLastCycleCounter = 0;
if( ulLastCycleCounter == ulCycleCounter )
{
xQueueSetPollStatus = pdFAIL;
}
ulLastCycleCounter = ulCycleCounter;
return xQueueSetPollStatus;
}
/*-----------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* A simple example that shows a stream buffer being used to pass data from an
* interrupt to a task.
*
* There are two strings, pcStringToSend and pcStringToReceive, where
* pcStringToReceive is a substring of pcStringToSend. The interrupt sends
* a few bytes of pcStringToSend to a stream buffer ever few times that it
* executes. A task reads the bytes from the stream buffer, looking for the
* substring, and flagging an error if the received data is invalid.
*/
/* Standard includes. */
#include "stdio.h"
#include "string.h"
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"
/* Demo app includes. */
#include "StreamBufferInterrupt.h"
#define sbiSTREAM_BUFFER_LENGTH_BYTES ( ( size_t ) 100 )
#define sbiSTREAM_BUFFER_TRIGGER_LEVEL_10 ( ( BaseType_t ) 10 )
/*-----------------------------------------------------------*/
/* Implements the task that receives a stream of bytes from the interrupt. */
static void prvReceivingTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* The stream buffer that is used to send data from an interrupt to the task. */
static StreamBufferHandle_t xStreamBuffer = NULL;
/* The string that is sent from the interrupt to the task four bytes at a
time. Must be multiple of 4 bytes long as the ISR sends 4 bytes at a time*/
static const char * pcStringToSend = "_____Hello FreeRTOS_____";
/* The string to task is looking for, which must be a substring of
pcStringToSend. */
static const char * pcStringToReceive = "Hello FreeRTOS";
/* Set to pdFAIL if anything unexpected happens. */
static BaseType_t xDemoStatus = pdPASS;
/* Incremented each time pcStringToReceive is correctly received, provided no
errors have occurred. Used so the check task can check this task is still
running as expected. */
static uint32_t ulCycleCount = 0;
/*-----------------------------------------------------------*/
void vStartStreamBufferInterruptDemo( void )
{
/* Create the stream buffer that sends data from the interrupt to the
task, and create the task. */
xStreamBuffer = xStreamBufferCreate( /* The buffer length in bytes. */
sbiSTREAM_BUFFER_LENGTH_BYTES,
/* The stream buffer's trigger level. */
sbiSTREAM_BUFFER_TRIGGER_LEVEL_10 );
xTaskCreate( prvReceivingTask, /* The function that implements the task. */
"StrIntRx", /* Human readable name for the task. */
configMINIMAL_STACK_SIZE, /* Stack size (in words!). */
NULL, /* Task parameter is not used. */
tskIDLE_PRIORITY + 2, /* The priority at which the task is created. */
NULL ); /* No use for the task handle. */
}
/*-----------------------------------------------------------*/
static void prvReceivingTask( void *pvParameters )
{
char cRxBuffer[ 20 ];
BaseType_t xNextByte = 0;
/* Remove warning about unused parameters. */
( void ) pvParameters;
/* Make sure the string will fit in the Rx buffer, including the NULL
terminator. */
configASSERT( sizeof( cRxBuffer ) > strlen( pcStringToReceive ) );
/* Make sure the stream buffer has been created. */
configASSERT( xStreamBuffer != NULL );
/* Start with the Rx buffer in a known state. */
memset( cRxBuffer, 0x00, sizeof( cRxBuffer ) );
for( ;; )
{
/* Keep receiving characters until the end of the string is received.
Note: An infinite block time is used to simplify the example. Infinite
block times are not recommended in production code as they do not allow
for error recovery. */
xStreamBufferReceive( /* The stream buffer data is being received from. */
xStreamBuffer,
/* Where to place received data. */
( void * ) &( cRxBuffer[ xNextByte ] ),
/* The number of bytes to receive. */
sizeof( char ),
/* The time to wait for the next data if the buffer
is empty. */
portMAX_DELAY );
/* If xNextByte is 0 then this task is looking for the start of the
string, which is 'H'. */
if( xNextByte == 0 )
{
if( cRxBuffer[ xNextByte ] == 'H' )
{
/* The start of the string has been found. Now receive
characters until the end of the string is found. */
xNextByte++;
}
}
else
{
/* Receiving characters while looking for the end of the string,
which is an 'S'. */
if( cRxBuffer[ xNextByte ] == 'S' )
{
/* The string has now been received. Check its validity. */
if( strcmp( cRxBuffer, pcStringToReceive ) != 0 )
{
xDemoStatus = pdFAIL;
}
/* Return to start looking for the beginning of the string
again. */
memset( cRxBuffer, 0x00, sizeof( cRxBuffer ) );
xNextByte = 0;
/* Increment the cycle count as an indication to the check task
that this demo is still running. */
if( xDemoStatus == pdPASS )
{
ulCycleCount++;
}
}
else
{
/* Receive the next character the next time around, while
continuing to look for the end of the string. */
xNextByte++;
configASSERT( ( size_t ) xNextByte < sizeof( cRxBuffer ) );
}
}
}
}
/*-----------------------------------------------------------*/
void vBasicStreamBufferSendFromISR( void )
{
static size_t xNextByteToSend = 0;
const BaseType_t xCallsBetweenSends = 100, xBytesToSend = 4;
static BaseType_t xCallCount = 0;
/* Is it time to write to the stream buffer again? */
xCallCount++;
if( xCallCount > xCallsBetweenSends )
{
xCallCount = 0;
/* Send the next four bytes to the stream buffer. */
xStreamBufferSendFromISR( xStreamBuffer,
( const void * ) ( pcStringToSend + xNextByteToSend ),
xBytesToSend,
NULL );
/* Send the next four bytes the next time around, wrapping to the start
of the string if necessary. */
xNextByteToSend += xBytesToSend;
if( xNextByteToSend >= strlen( pcStringToSend ) )
{
xNextByteToSend = 0;
}
}
}
/*-----------------------------------------------------------*/
BaseType_t xIsInterruptStreamBufferDemoStillRunning( void )
{
uint32_t ulLastCycleCount = 0;
/* Check the demo is still running. */
if( ulLastCycleCount == ulCycleCount )
{
xDemoStatus = pdFAIL;
}
else
{
ulLastCycleCount = ulCycleCount;
}
return xDemoStatus;
}

View File

@ -0,0 +1,693 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Tests the behaviour of direct task notifications.
*/
/* Standard includes. */
#include <limits.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
/* Demo program include files. */
#include "TaskNotify.h"
/* Allow parameters to be overridden on a demo by demo basis. */
#ifndef notifyNOTIFIED_TASK_STACK_SIZE
#define notifyNOTIFIED_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
#endif
#define notifyTASK_PRIORITY ( tskIDLE_PRIORITY )
#define notifyUINT32_MAX ( ( uint32_t ) 0xffffffff )
#define notifySUSPENDED_TEST_TIMER_PERIOD pdMS_TO_TICKS( 50 )
/*-----------------------------------------------------------*/
/*
* Implementation of the task that gets notified.
*/
static void prvNotifiedTask( void *pvParameters );
/*
* Performs a few initial tests that can be done prior to creating the second
* task.
*/
static void prvSingleTaskTests( void );
/*
* Software timer callback function from which xTaskNotify() is called.
*/
static void prvNotifyingTimer( TimerHandle_t xTimer );
/*
* Utility function to create pseudo random numbers.
*/
static UBaseType_t prvRand( void );
/*
* Callback for a timer that is used during preliminary testing. The timer
* tests the behaviour when 1: a task waiting for a notification is suspended
* and then resumed without ever receiving a notification, and 2: when a task
* waiting for a notification receives a notification while it is suspended.
*/
static void prvSuspendedTaskTimerTestCallback( TimerHandle_t xExpiredTimer );
/*-----------------------------------------------------------*/
/* Used to latch errors during the test's execution. */
static BaseType_t xErrorStatus = pdPASS;
/* Used to ensure the task has not stalled. */
static volatile uint32_t ulNotifyCycleCount = 0;
/* The handle of the task that receives the notifications. */
static TaskHandle_t xTaskToNotify = NULL;
/* Used to count the notifications sent to the task from a software timer and
the number of notifications received by the task from the software timer. The
two should stay synchronised. */
static uint32_t ulTimerNotificationsReceived = 0UL, ulTimerNotificationsSent = 0UL;
/* The timer used to notify the task. */
static TimerHandle_t xTimer = NULL;
/* Used by the pseudo random number generating function. */
static size_t uxNextRand = 0;
/*-----------------------------------------------------------*/
void vStartTaskNotifyTask( void )
{
/* Create the task that performs some tests by itself, then loops around
being notified by both a software timer and an interrupt. */
xTaskCreate( prvNotifiedTask, /* Function that implements the task. */
"Notified", /* Text name for the task - for debugging only - not used by the kernel. */
notifyNOTIFIED_TASK_STACK_SIZE, /* Task's stack size in words, not bytes!. */
NULL, /* Task parameter, not used in this case. */
notifyTASK_PRIORITY, /* Task priority, 0 is the lowest. */
&xTaskToNotify ); /* Used to pass a handle to the task out is needed, otherwise set to NULL. */
/* Pseudo seed the random number generator. */
uxNextRand = ( size_t ) prvRand;
}
/*-----------------------------------------------------------*/
static void prvSingleTaskTests( void )
{
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
BaseType_t xReturned;
uint32_t ulNotifiedValue, ulLoop, ulNotifyingValue, ulPreviousValue, ulExpectedValue;
TickType_t xTimeOnEntering;
const uint32_t ulFirstNotifiedConst = 100001UL, ulSecondNotifiedValueConst = 5555UL, ulMaxLoops = 5UL;
const uint32_t ulBit0 = 0x01UL, ulBit1 = 0x02UL;
TimerHandle_t xSingleTaskTimer;
/* ------------------------------------------------------------------------
Check blocking when there are no notifications. */
xTimeOnEntering = xTaskGetTickCount();
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, xTicksToWait );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Should have blocked for the entire block time. */
if( ( xTaskGetTickCount() - xTimeOnEntering ) < xTicksToWait )
{
xErrorStatus = pdFAIL;
}
configASSERT( xReturned == pdFAIL );
configASSERT( ulNotifiedValue == 0UL );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) ulNotifiedValue;
/* ------------------------------------------------------------------------
Check no blocking when notifications are pending. First notify itself -
this would not be a normal thing to do and is done here for test purposes
only. */
xReturned = xTaskNotifyAndQuery( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite, &ulPreviousValue );
/* Even through the 'without overwrite' action was used the update should
have been successful. */
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* No bits should have been pending previously. */
configASSERT( ulPreviousValue == 0 );
( void ) ulPreviousValue;
/* The task should now have a notification pending, and so not time out. */
xTimeOnEntering = xTaskGetTickCount();
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, xTicksToWait );
if( ( xTaskGetTickCount() - xTimeOnEntering ) >= xTicksToWait )
{
xErrorStatus = pdFAIL;
}
/* The task should have been notified, and the notified value should
be equal to ulFirstNotifiedConst. */
configASSERT( xReturned == pdPASS );
configASSERT( ulNotifiedValue == ulFirstNotifiedConst );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) ulNotifiedValue;
/* Incremented to show the task is still running. */
ulNotifyCycleCount++;
/*-------------------------------------------------------------------------
Check the non-overwriting functionality. The notification is done twice
using two different notification values. The action says don't overwrite so
only the first notification should pass and the value read back should also
be that used with the first notification. */
xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithoutOverwrite );
configASSERT( xReturned == pdFAIL );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Waiting for the notification should now return immediately so a block
time of zero is used. */
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdPASS );
configASSERT( ulNotifiedValue == ulFirstNotifiedConst );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) ulNotifiedValue;
/*-------------------------------------------------------------------------
Do the same again, only this time use the overwriting version. This time
both notifications should pass, and the value written the second time should
overwrite the value written the first time, and so be the value that is read
back. */
xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithOverwrite );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithOverwrite );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst );
( void ) ulNotifiedValue;
/*-------------------------------------------------------------------------
Check notifications with no action pass without updating the value. Even
though ulFirstNotifiedConst is used as the value the value read back should
remain at ulSecondNotifiedConst. */
xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eNoAction );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 );
configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst );
( void ) ulNotifiedValue; /* In case configASSERT() is not defined. */
/*-------------------------------------------------------------------------
Check incrementing values. Send ulMaxLoop increment notifications, then
ensure the received value is as expected - which should be
ulSecondNotificationValueConst plus how ever many times to loop iterated. */
for( ulLoop = 0; ulLoop < ulMaxLoops; ulLoop++ )
{
xReturned = xTaskNotify( xTaskToNotify, 0, eIncrement );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
}
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdPASS );
configASSERT( ulNotifiedValue == ( ulSecondNotifiedValueConst + ulMaxLoops ) );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) ulNotifiedValue;
/* Should not be any notifications pending now. */
xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdFAIL );
( void ) xReturned; /* In case configASSERT() is not defined. */
( void ) ulNotifiedValue;
/*-------------------------------------------------------------------------
Check all bits can be set by notifying the task with one additional bit set
on each notification, and exiting the loop when all the bits are found to be
set. As there are 32-bits the loop should execute 32 times before all the
bits are found to be set. */
ulNotifyingValue = 0x01;
ulLoop = 0;
/* Start with all bits clear. */
xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 );
do
{
/* Set the next bit in the task's notified value. */
xTaskNotify( xTaskToNotify, ulNotifyingValue, eSetBits );
/* Wait for the notified value - which of course will already be
available. Don't clear the bits on entry or exit as this loop is exited
when all the bits are set. */
xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
ulLoop++;
/* Use the next bit on the next iteration around this loop. */
ulNotifyingValue <<= 1UL;
} while ( ulNotifiedValue != notifyUINT32_MAX );
/* As a 32-bit value was used the loop should have executed 32 times before
all the bits were set. */
configASSERT( ulLoop == 32 );
/*-------------------------------------------------------------------------
Check bits are cleared on entry but not on exit when a notification fails
to arrive before timing out - both with and without a timeout value. Wait
for the notification again - but this time it is not given by anything and
should return pdFAIL. The parameters are set to clear bit zero on entry and
bit one on exit. As no notification was received only the bit cleared on
entry should actually get cleared. */
xReturned = xTaskNotifyWait( ulBit0, ulBit1, &ulNotifiedValue, xTicksToWait );
configASSERT( xReturned == pdFAIL );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Notify the task with no action so as not to update the bits even though
notifyUINT32_MAX is used as the notification value. */
xTaskNotify( xTaskToNotify, notifyUINT32_MAX, eNoAction );
/* Reading back the value should should find bit 0 is clear, as this was
cleared on entry, but bit 1 is not clear as it will not have been cleared on
exit as no notification was received. */
xReturned = xTaskNotifyWait( 0x00UL, 0x00UL, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdPASS );
configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~ulBit0 ) );
( void ) xReturned; /* In case configASSERT() is not defined. */
/*-------------------------------------------------------------------------
Now try clearing the bit on exit. For that to happen a notification must be
received, so the task is notified first. */
xTaskNotify( xTaskToNotify, 0, eNoAction );
xTaskNotifyWait( 0x00, ulBit1, &ulNotifiedValue, 0 );
/* However as the bit is cleared on exit, after the returned notification
value is set, the returned notification value should not have the bit
cleared... */
configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~ulBit0 ) );
/* ...but reading the value back again should find that the bit was indeed
cleared internally. The returned value should be pdFAIL however as nothing
has notified the task in the mean time. */
xReturned = xTaskNotifyWait( 0x00, 0x00, &ulNotifiedValue, 0 );
configASSERT( xReturned == pdFAIL );
configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~( ulBit0 | ulBit1 ) ) );
( void ) xReturned; /* In case configASSERT() is not defined. */
/*-------------------------------------------------------------------------
Now try querying the previous value while notifying a task. */
xTaskNotifyAndQuery( xTaskToNotify, 0x00, eSetBits, &ulPreviousValue );
configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~( ulBit0 | ulBit1 ) ) );
/* Clear all bits. */
xTaskNotifyWait( 0x00, notifyUINT32_MAX, &ulNotifiedValue, 0 );
xTaskNotifyAndQuery( xTaskToNotify, 0x00, eSetBits, &ulPreviousValue );
configASSERT( ulPreviousValue == 0 );
ulExpectedValue = 0;
for( ulLoop = 0x01; ulLoop < 0x80UL; ulLoop <<= 1UL )
{
/* Set the next bit up, and expect to receive the last bits set (so
the previous value will not yet have the bit being set this time
around). */
xTaskNotifyAndQuery( xTaskToNotify, ulLoop, eSetBits, &ulPreviousValue );
configASSERT( ulExpectedValue == ulPreviousValue );
ulExpectedValue |= ulLoop;
}
/* ------------------------------------------------------------------------
Clear the previous notifications. */
xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 );
/* The task should not have any notifications pending, so an attempt to clear
the notification state should fail. */
configASSERT( xTaskNotifyStateClear( NULL ) == pdFALSE );
/* Get the task to notify itself. This is not a normal thing to do, and is
only done here for test purposes. */
xTaskNotifyAndQuery( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite, &ulPreviousValue );
/* Now the notification state should be eNotified, so it should now be
possible to clear the notification state. */
configASSERT( xTaskNotifyStateClear( NULL ) == pdTRUE );
configASSERT( xTaskNotifyStateClear( NULL ) == pdFALSE );
/* ------------------------------------------------------------------------
Create a timer that will try notifying this task while it is suspended. */
xSingleTaskTimer = xTimerCreate( "SingleNotify", notifySUSPENDED_TEST_TIMER_PERIOD, pdFALSE, NULL, prvSuspendedTaskTimerTestCallback );
configASSERT( xSingleTaskTimer );
/* Incremented to show the task is still running. */
ulNotifyCycleCount++;
/* Ensure no notifications are pending. */
xTaskNotifyWait( notifyUINT32_MAX, 0, NULL, 0 );
/* Raise the task's priority so it can suspend itself before the timer
expires. */
vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );
/* Start the timer that will try notifying this task while it is
suspended, then wait for a notification. The first time the callback
executes the timer will suspend the task, then resume the task, without
ever sending a notification to the task. */
ulNotifiedValue = 0;
xTimerStart( xSingleTaskTimer, portMAX_DELAY );
/* Check a notification is not received. */
xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, portMAX_DELAY );
configASSERT( xReturned == pdFALSE );
configASSERT( ulNotifiedValue == 0 );
( void ) xReturned; /* In case configASSERT() is not defined. */
/* Incremented to show the task is still running. */
ulNotifyCycleCount++;
/* Start the timer that will try notifying this task while it is
suspended, then wait for a notification. The second time the callback
executes the timer will suspend the task, notify the task, then resume the
task (previously it was suspended and resumed without being notified). */
xTimerStart( xSingleTaskTimer, portMAX_DELAY );
/* Check a notification is received. */
xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, portMAX_DELAY );
configASSERT( xReturned == pdPASS );
( void ) xReturned; /* In case configASSERT() is not defined. */
configASSERT( ulNotifiedValue != 0 );
/* Return the task to its proper priority and delete the timer as it is
not used again. */
vTaskPrioritySet( NULL, notifyTASK_PRIORITY );
xTimerDelete( xSingleTaskTimer, portMAX_DELAY );
/* Incremented to show the task is still running. */
ulNotifyCycleCount++;
/* Leave all bits cleared. */
xTaskNotifyWait( notifyUINT32_MAX, 0, NULL, 0 );
}
/*-----------------------------------------------------------*/
static void prvSuspendedTaskTimerTestCallback( TimerHandle_t xExpiredTimer )
{
static uint32_t ulCallCount = 0;
/* Remove compiler warnings about unused parameters. */
( void ) xExpiredTimer;
/* Callback for a timer that is used during preliminary testing. The timer
tests the behaviour when 1: a task waiting for a notification is suspended
and then resumed without ever receiving a notification, and 2: when a task
waiting for a notification receives a notification while it is suspended. */
if( ulCallCount == 0 )
{
vTaskSuspend( xTaskToNotify );
configASSERT( eTaskGetState( xTaskToNotify ) == eSuspended );
vTaskResume( xTaskToNotify );
}
else
{
vTaskSuspend( xTaskToNotify );
/* Sending a notification while the task is suspended should pass, but
not cause the task to resume. ulCallCount is just used as a convenient
non-zero value. */
xTaskNotify( xTaskToNotify, ulCallCount, eSetValueWithOverwrite );
/* Make sure giving the notification didn't resume the task. */
configASSERT( eTaskGetState( xTaskToNotify ) == eSuspended );
vTaskResume( xTaskToNotify );
}
ulCallCount++;
}
/*-----------------------------------------------------------*/
static void prvNotifyingTimer( TimerHandle_t xNotUsed )
{
( void ) xNotUsed;
xTaskNotifyGive( xTaskToNotify );
/* This value is also incremented from an interrupt. */
taskENTER_CRITICAL();
{
ulTimerNotificationsSent++;
}
taskEXIT_CRITICAL();
}
/*-----------------------------------------------------------*/
static void prvNotifiedTask( void *pvParameters )
{
const TickType_t xMaxPeriod = pdMS_TO_TICKS( 90 ), xMinPeriod = pdMS_TO_TICKS( 10 ), xDontBlock = 0;
TickType_t xPeriod;
const uint32_t ulCyclesToRaisePriority = 50UL;
/* Remove compiler warnings about unused parameters. */
( void ) pvParameters;
/* Run a few tests that can be done from a single task before entering the
main loop. */
prvSingleTaskTests();
/* Create the software timer that is used to send notifications to this
task. Notifications are also received from an interrupt. */
xTimer = xTimerCreate( "Notifier", xMaxPeriod, pdFALSE, NULL, prvNotifyingTimer );
for( ;; )
{
/* Start the timer again with a different period. Sometimes the period
will be higher than the task's block time, sometimes it will be lower
than the task's block time. */
xPeriod = prvRand() % xMaxPeriod;
if( xPeriod < xMinPeriod )
{
xPeriod = xMinPeriod;
}
/* Change the timer period and start the timer. */
xTimerChangePeriod( xTimer, xPeriod, portMAX_DELAY );
/* Block waiting for the notification again with a different period.
Sometimes the period will be higher than the task's block time,
sometimes it will be lower than the task's block time. */
xPeriod = prvRand() % xMaxPeriod;
if( xPeriod < xMinPeriod )
{
xPeriod = xMinPeriod;
}
/* Block to wait for a notification but without clearing the
notification count, so only add one to the count of received
notifications as any other notifications will remain pending. */
if( ulTaskNotifyTake( pdFALSE, xPeriod ) != 0 )
{
ulTimerNotificationsReceived++;
}
/* Take a notification without clearing again, but this time without a
block time specified. */
if( ulTaskNotifyTake( pdFALSE, xDontBlock ) != 0 )
{
ulTimerNotificationsReceived++;
}
/* Wait for the next notification from the timer, clearing all
notifications if one is received, so this time adding the total number
of notifications that were pending as none will be left pending after
the function call. */
ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, xPeriod );
/* Occasionally raise the priority of the task being notified to test
the path where the task is notified from an ISR and becomes the highest
priority ready state task, but the pxHigherPriorityTaskWoken parameter
is NULL (which it is in the tick hook that sends notifications to this
task). */
if( ( ulNotifyCycleCount % ulCyclesToRaisePriority ) == 0 )
{
vTaskPrioritySet( xTaskToNotify, configMAX_PRIORITIES - 1 );
/* Wait for the next notification again, clearing all notifications
if one is received, but this time blocking indefinitely. */
ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
/* Reset the priority. */
vTaskPrioritySet( xTaskToNotify, notifyTASK_PRIORITY );
}
else
{
/* Wait for the next notification again, clearing all notifications
if one is received, but this time blocking indefinitely. */
ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
}
/* Incremented to show the task is still running. */
ulNotifyCycleCount++;
}
}
/*-----------------------------------------------------------*/
void xNotifyTaskFromISR( void )
{
static BaseType_t xCallCount = 0, xAPIToUse = 0;
const BaseType_t xCallInterval = pdMS_TO_TICKS( 50 );
uint32_t ulPreviousValue;
const uint32_t ulUnexpectedValue = 0xff;
/* The task performs some tests before starting the timer that gives the
notification from this interrupt. If the timer has not been created yet
then the initial tests have not yet completed and the notification should
not be sent. */
if( xTimer != NULL )
{
xCallCount++;
if( xCallCount >= xCallInterval )
{
/* It is time to 'give' the notification again. */
xCallCount = 0;
/* Test using both vTaskNotifyGiveFromISR(), xTaskNotifyFromISR()
and xTaskNotifyAndQueryFromISR(). */
switch( xAPIToUse )
{
case 0: vTaskNotifyGiveFromISR( xTaskToNotify, NULL );
xAPIToUse++;
break;
case 1: xTaskNotifyFromISR( xTaskToNotify, 0, eIncrement, NULL );
xAPIToUse++;
break;
case 2: ulPreviousValue = ulUnexpectedValue;
xTaskNotifyAndQueryFromISR( xTaskToNotify, 0, eIncrement, &ulPreviousValue, NULL );
configASSERT( ulPreviousValue != ulUnexpectedValue );
xAPIToUse = 0;
break;
default:/* Should never get here!. */
break;
}
ulTimerNotificationsSent++;
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check the created tasks are still running and have not
detected any errors. */
BaseType_t xAreTaskNotificationTasksStillRunning( void )
{
static uint32_t ulLastNotifyCycleCount = 0;
const uint32_t ulMaxSendReceiveDeviation = 5UL;
/* Check the cycle count is still incrementing to ensure the task is still
actually running. */
if( ulLastNotifyCycleCount == ulNotifyCycleCount )
{
xErrorStatus = pdFAIL;
}
else
{
ulLastNotifyCycleCount = ulNotifyCycleCount;
}
/* Check the count of 'takes' from the software timer is keeping track with
the amount of 'gives'. */
if( ulTimerNotificationsSent > ulTimerNotificationsReceived )
{
if( ( ulTimerNotificationsSent - ulTimerNotificationsReceived ) > ulMaxSendReceiveDeviation )
{
xErrorStatus = pdFAIL;
}
}
return xErrorStatus;
}
/*-----------------------------------------------------------*/
static UBaseType_t prvRand( void )
{
const size_t uxMultiplier = ( size_t ) 0x015a4e35, uxIncrement = ( size_t ) 1;
/* Utility function to generate a pseudo random number. */
uxNextRand = ( uxMultiplier * uxNextRand ) + uxIncrement;
return( ( uxNextRand >> 16 ) & ( ( size_t ) 0x7fff ) );
}
/*-----------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,544 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This file contains some test scenarios that ensure tasks do not exit queue
* send or receive functions prematurely. A description of the tests is
* included within the code.
*/
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo includes. */
#include "blocktim.h"
/* Task priorities and stack sizes. Allow these to be overridden. */
#ifndef bktPRIMARY_PRIORITY
#define bktPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 )
#endif
#ifndef bktSECONDARY_PRIORITY
#define bktSECONDARY_PRIORITY ( configMAX_PRIORITIES - 4 )
#endif
#ifndef bktBLOCK_TIME_TASK_STACK_SIZE
#define bktBLOCK_TIME_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
#endif
/* Task behaviour. */
#define bktQUEUE_LENGTH ( 5 )
#define bktSHORT_WAIT pdMS_TO_TICKS( ( TickType_t ) 20 )
#define bktPRIMARY_BLOCK_TIME ( 10 )
#define bktALLOWABLE_MARGIN ( 15 )
#define bktTIME_TO_BLOCK ( 175 )
#define bktDONT_BLOCK ( ( TickType_t ) 0 )
#define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 )
/* In case the demo does not have software timers enabled, as this file uses
the configTIMER_TASK_PRIORITY setting. */
#ifndef configTIMER_TASK_PRIORITY
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
#endif
/*-----------------------------------------------------------*/
/*
* The two test tasks. Their behaviour is commented within the functions.
*/
static void vPrimaryBlockTimeTestTask( void *pvParameters );
static void vSecondaryBlockTimeTestTask( void *pvParameters );
/*
* Very basic tests to verify the block times are as expected.
*/
static void prvBasicDelayTests( void );
/*-----------------------------------------------------------*/
/* The queue on which the tasks block. */
static QueueHandle_t xTestQueue;
/* Handle to the secondary task is required by the primary task for calls
to vTaskSuspend/Resume(). */
static TaskHandle_t xSecondary;
/* Used to ensure that tasks are still executing without error. */
static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0;
static volatile BaseType_t xErrorOccurred = pdFALSE;
/* Provides a simple mechanism for the primary task to know when the
secondary task has executed. */
static volatile UBaseType_t xRunIndicator;
/*-----------------------------------------------------------*/
void vCreateBlockTimeTasks( void )
{
/* Create the queue on which the two tasks block. */
xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );
if( xTestQueue != NULL )
{
/* vQueueAddToRegistry() adds the queue to the queue registry, if one
is in use. The queue registry is provided as a means for kernel aware
debuggers to locate queues and has no purpose if a kernel aware
debugger is not being used. The call to vQueueAddToRegistry() will be
removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
defined or is defined to be less than 1. */
vQueueAddToRegistry( xTestQueue, "Block_Time_Queue" );
/* Create the two test tasks. */
xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );
xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );
}
}
/*-----------------------------------------------------------*/
static void vPrimaryBlockTimeTestTask( void *pvParameters )
{
BaseType_t xItem, xData;
TickType_t xTimeWhenBlocking;
TickType_t xTimeToBlock, xBlockedTime;
( void ) pvParameters;
for( ;; )
{
/*********************************************************************
Test 0
Basic vTaskDelay() and vTaskDelayUntil() tests. */
prvBasicDelayTests();
/*********************************************************************
Test 1
Simple block time wakeup test on queue receives. */
for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
{
/* The queue is empty. Attempt to read from the queue using a block
time. When we wake, ensure the delta in time is as expected. */
xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
xTimeWhenBlocking = xTaskGetTickCount();
/* We should unblock after xTimeToBlock having not received
anything on the queue. */
if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
{
xErrorOccurred = pdTRUE;
}
/* How long were we blocked for? */
xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
if( xBlockedTime < xTimeToBlock )
{
/* Should not have blocked for less than we requested. */
xErrorOccurred = pdTRUE;
}
if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
{
/* Should not have blocked for longer than we requested,
although we would not necessarily run as soon as we were
unblocked so a margin is allowed. */
xErrorOccurred = pdTRUE;
}
}
/*********************************************************************
Test 2
Simple block time wakeup test on queue sends.
First fill the queue. It should be empty so all sends should pass. */
for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
{
if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
{
/* The queue is full. Attempt to write to the queue using a block
time. When we wake, ensure the delta in time is as expected. */
xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
xTimeWhenBlocking = xTaskGetTickCount();
/* We should unblock after xTimeToBlock having not received
anything on the queue. */
if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
{
xErrorOccurred = pdTRUE;
}
/* How long were we blocked for? */
xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
if( xBlockedTime < xTimeToBlock )
{
/* Should not have blocked for less than we requested. */
xErrorOccurred = pdTRUE;
}
if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
{
/* Should not have blocked for longer than we requested,
although we would not necessarily run as soon as we were
unblocked so a margin is allowed. */
xErrorOccurred = pdTRUE;
}
}
/*********************************************************************
Test 3
Wake the other task, it will block attempting to post to the queue.
When we read from the queue the other task will wake, but before it
can run we will post to the queue again. When the other task runs it
will find the queue still full, even though it was woken. It should
recognise that its block time has not expired and return to block for
the remains of its block time.
Wake the other task so it blocks attempting to post to the already
full queue. */
xRunIndicator = 0;
vTaskResume( xSecondary );
/* We need to wait a little to ensure the other task executes. */
while( xRunIndicator != bktRUN_INDICATOR )
{
/* The other task has not yet executed. */
vTaskDelay( bktSHORT_WAIT );
}
/* Make sure the other task is blocked on the queue. */
vTaskDelay( bktSHORT_WAIT );
xRunIndicator = 0;
for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
{
/* Now when we make space on the queue the other task should wake
but not execute as this task has higher priority. */
if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
/* Now fill the queue again before the other task gets a chance to
execute. If the other task had executed we would find the queue
full ourselves, and the other task have set xRunIndicator. */
if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
if( xRunIndicator == bktRUN_INDICATOR )
{
/* The other task should not have executed. */
xErrorOccurred = pdTRUE;
}
/* Raise the priority of the other task so it executes and blocks
on the queue again. */
vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
/* The other task should now have re-blocked without exiting the
queue function. */
if( xRunIndicator == bktRUN_INDICATOR )
{
/* The other task should not have executed outside of the
queue function. */
xErrorOccurred = pdTRUE;
}
/* Set the priority back down. */
vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
}
/* Let the other task timeout. When it unblockes it will check that it
unblocked at the correct time, then suspend itself. */
while( xRunIndicator != bktRUN_INDICATOR )
{
vTaskDelay( bktSHORT_WAIT );
}
vTaskDelay( bktSHORT_WAIT );
xRunIndicator = 0;
/*********************************************************************
Test 4
As per test 3 - but with the send and receive the other way around.
The other task blocks attempting to read from the queue.
Empty the queue. We should find that it is full. */
for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
{
if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
}
/* Wake the other task so it blocks attempting to read from the
already empty queue. */
vTaskResume( xSecondary );
/* We need to wait a little to ensure the other task executes. */
while( xRunIndicator != bktRUN_INDICATOR )
{
vTaskDelay( bktSHORT_WAIT );
}
vTaskDelay( bktSHORT_WAIT );
xRunIndicator = 0;
for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
{
/* Now when we place an item on the queue the other task should
wake but not execute as this task has higher priority. */
if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
/* Now empty the queue again before the other task gets a chance to
execute. If the other task had executed we would find the queue
empty ourselves, and the other task would be suspended. */
if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
if( xRunIndicator == bktRUN_INDICATOR )
{
/* The other task should not have executed. */
xErrorOccurred = pdTRUE;
}
/* Raise the priority of the other task so it executes and blocks
on the queue again. */
vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
/* The other task should now have re-blocked without exiting the
queue function. */
if( xRunIndicator == bktRUN_INDICATOR )
{
/* The other task should not have executed outside of the
queue function. */
xErrorOccurred = pdTRUE;
}
vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
}
/* Let the other task timeout. When it unblockes it will check that it
unblocked at the correct time, then suspend itself. */
while( xRunIndicator != bktRUN_INDICATOR )
{
vTaskDelay( bktSHORT_WAIT );
}
vTaskDelay( bktSHORT_WAIT );
xPrimaryCycles++;
}
}
/*-----------------------------------------------------------*/
static void vSecondaryBlockTimeTestTask( void *pvParameters )
{
TickType_t xTimeWhenBlocking, xBlockedTime;
BaseType_t xData;
( void ) pvParameters;
for( ;; )
{
/*********************************************************************
Test 0, 1 and 2
This task does not participate in these tests. */
vTaskSuspend( NULL );
/*********************************************************************
Test 3
The first thing we do is attempt to read from the queue. It should be
full so we block. Note the time before we block so we can check the
wake time is as per that expected. */
xTimeWhenBlocking = xTaskGetTickCount();
/* We should unblock after bktTIME_TO_BLOCK having not sent anything to
the queue. */
xData = 0;
xRunIndicator = bktRUN_INDICATOR;
if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
{
xErrorOccurred = pdTRUE;
}
/* How long were we inside the send function? */
xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
/* We should not have blocked for less time than bktTIME_TO_BLOCK. */
if( xBlockedTime < bktTIME_TO_BLOCK )
{
xErrorOccurred = pdTRUE;
}
/* We should of not blocked for much longer than bktALLOWABLE_MARGIN
either. A margin is permitted as we would not necessarily run as
soon as we unblocked. */
if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
{
xErrorOccurred = pdTRUE;
}
/* Suspend ready for test 3. */
xRunIndicator = bktRUN_INDICATOR;
vTaskSuspend( NULL );
/*********************************************************************
Test 4
As per test three, but with the send and receive reversed. */
xTimeWhenBlocking = xTaskGetTickCount();
/* We should unblock after bktTIME_TO_BLOCK having not received
anything on the queue. */
xRunIndicator = bktRUN_INDICATOR;
if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
{
xErrorOccurred = pdTRUE;
}
xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
/* We should not have blocked for less time than bktTIME_TO_BLOCK. */
if( xBlockedTime < bktTIME_TO_BLOCK )
{
xErrorOccurred = pdTRUE;
}
/* We should of not blocked for much longer than bktALLOWABLE_MARGIN
either. A margin is permitted as we would not necessarily run as soon
as we unblocked. */
if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
{
xErrorOccurred = pdTRUE;
}
xRunIndicator = bktRUN_INDICATOR;
xSecondaryCycles++;
}
}
/*-----------------------------------------------------------*/
static void prvBasicDelayTests( void )
{
TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime;
const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 );
/* Temporarily increase priority so the timing is more accurate, but not so
high as to disrupt the timer tests. */
vTaskPrioritySet( NULL, configTIMER_TASK_PRIORITY - 1 );
/* Crude check to too that vTaskDelay() blocks for the expected period. */
xPreTime = xTaskGetTickCount();
vTaskDelay( bktTIME_TO_BLOCK );
xPostTime = xTaskGetTickCount();
/* The priority is higher, so the allowable margin is halved when compared
to the other tests in this file. */
if( ( xPostTime - xPreTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
{
xErrorOccurred = pdTRUE;
}
/* Now crude tests to check the vTaskDelayUntil() functionality. */
xPostTime = xTaskGetTickCount();
xLastUnblockTime = xPostTime;
for( x = 0; x < xCycles; x++ )
{
/* Calculate the next expected unblock time from the time taken before
this loop was entered. */
xExpectedUnblockTime = xPostTime + ( x * xPeriod );
vTaskDelayUntil( &xLastUnblockTime, xPeriod );
if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
{
xErrorOccurred = pdTRUE;
}
xPrimaryCycles++;
}
/* Reset to the original task priority ready for the other tests. */
vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY );
}
/*-----------------------------------------------------------*/
BaseType_t xAreBlockTimeTestTasksStillRunning( void )
{
static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
BaseType_t xReturn = pdPASS;
/* Have both tasks performed at least one cycle since this function was
last called? */
if( xPrimaryCycles == xLastPrimaryCycleCount )
{
xReturn = pdFAIL;
}
if( xSecondaryCycles == xLastSecondaryCycleCount )
{
xReturn = pdFAIL;
}
if( xErrorOccurred == pdTRUE )
{
xReturn = pdFAIL;
}
xLastSecondaryCycleCount = xSecondaryCycles;
xLastPrimaryCycleCount = xPrimaryCycles;
return xReturn;
}

View File

@ -0,0 +1,265 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This version of comtest. c is for use on systems that have limited stack
* space and no display facilities. The complete version can be found in
* the Demo/Common/Full directory.
*
* Creates two tasks that operate on an interrupt driven serial port. A
* loopback connector should be used so that everything that is transmitted is
* also received. The serial port does not use any flow control. On a
* standard 9way 'D' connector pins two and three should be connected together.
*
* The first task posts a sequence of characters to the Tx queue, toggling an
* LED on each successful post. At the end of the sequence it sleeps for a
* pseudo-random period before resending the same sequence.
*
* The UART Tx end interrupt is enabled whenever data is available in the Tx
* queue. The Tx end ISR removes a single character from the Tx queue and
* passes it to the UART for transmission.
*
* The second task blocks on the Rx queue waiting for a character to become
* available. When the UART Rx end interrupt receives a character it places
* it in the Rx queue, waking the second task. The second task checks that the
* characters removed from the Rx queue form the same sequence as those posted
* to the Tx queue, and toggles an LED for each correct character.
*
* The receiving task is spawned with a higher priority than the transmitting
* task. The receiver will therefore wake every time a character is
* transmitted so neither the Tx or Rx queue should ever hold more than a few
* characters.
*
*/
/* Scheduler include files. */
#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "serial.h"
#include "comtest.h"
#include "partest.h"
#define comSTACK_SIZE configMINIMAL_STACK_SIZE
#define comTX_LED_OFFSET ( 0 )
#define comRX_LED_OFFSET ( 1 )
#define comTOTAL_PERMISSIBLE_ERRORS ( 2 )
/* The Tx task will transmit the sequence of characters at a pseudo random
interval. This is the maximum and minimum block time between sends. */
#define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x96 )
#define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0x32 )
#define comOFFSET_TIME ( ( TickType_t ) 3 )
/* We should find that each character can be queued for Tx immediately and we
don't have to block to send. */
#define comNO_BLOCK ( ( TickType_t ) 0 )
/* The Rx task will block on the Rx queue for a long period. */
#define comRX_BLOCK_TIME ( ( TickType_t ) 0xffff )
/* The sequence transmitted is from comFIRST_BYTE to and including comLAST_BYTE. */
#define comFIRST_BYTE ( 'A' )
#define comLAST_BYTE ( 'X' )
#define comBUFFER_LEN ( ( UBaseType_t ) ( comLAST_BYTE - comFIRST_BYTE ) + ( UBaseType_t ) 1 )
#define comINITIAL_RX_COUNT_VALUE ( 0 )
/* Handle to the com port used by both tasks. */
static xComPortHandle xPort = NULL;
/* The transmit task as described at the top of the file. */
static portTASK_FUNCTION_PROTO( vComTxTask, pvParameters );
/* The receive task as described at the top of the file. */
static portTASK_FUNCTION_PROTO( vComRxTask, pvParameters );
/* The LED that should be toggled by the Rx and Tx tasks. The Rx task will
toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task will toggle LED
( uxBaseLED + comTX_LED_OFFSET ). */
static UBaseType_t uxBaseLED = 0;
/* Check variable used to ensure no error have occurred. The Rx task will
increment this variable after every successfully received sequence. If at any
time the sequence is incorrect the the variable will stop being incremented. */
static volatile UBaseType_t uxRxLoops = comINITIAL_RX_COUNT_VALUE;
/*-----------------------------------------------------------*/
void vAltStartComTestTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED )
{
/* Initialise the com port then spawn the Rx and Tx tasks. */
uxBaseLED = uxLED;
xSerialPortInitMinimal( ulBaudRate, comBUFFER_LEN );
/* The Tx task is spawned with a lower priority than the Rx task. */
xTaskCreate( vComTxTask, "COMTx", comSTACK_SIZE, NULL, uxPriority - 1, ( TaskHandle_t * ) NULL );
xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL );
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vComTxTask, pvParameters )
{
char cByteToSend;
TickType_t xTimeToWait;
/* Just to stop compiler warnings. */
( void ) pvParameters;
for( ;; )
{
/* Simply transmit a sequence of characters from comFIRST_BYTE to
comLAST_BYTE. */
for( cByteToSend = comFIRST_BYTE; cByteToSend <= comLAST_BYTE; cByteToSend++ )
{
if( xSerialPutChar( xPort, cByteToSend, comNO_BLOCK ) == pdPASS )
{
vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET );
}
}
/* Turn the LED off while we are not doing anything. */
vParTestSetLED( uxBaseLED + comTX_LED_OFFSET, pdFALSE );
/* We have posted all the characters in the string - wait before
re-sending. Wait a pseudo-random time as this will provide a better
test. */
xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;
/* Make sure we don't wait too long... */
xTimeToWait %= comTX_MAX_BLOCK_TIME;
/* ...but we do want to wait. */
if( xTimeToWait < comTX_MIN_BLOCK_TIME )
{
xTimeToWait = comTX_MIN_BLOCK_TIME;
}
vTaskDelay( xTimeToWait );
}
} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vComRxTask, pvParameters )
{
signed char cExpectedByte, cByteRxed;
BaseType_t xResyncRequired = pdFALSE, xErrorOccurred = pdFALSE;
/* Just to stop compiler warnings. */
( void ) pvParameters;
for( ;; )
{
/* We expect to receive the characters from comFIRST_BYTE to
comLAST_BYTE in an incrementing order. Loop to receive each byte. */
for( cExpectedByte = comFIRST_BYTE; cExpectedByte <= comLAST_BYTE; cExpectedByte++ )
{
/* Block on the queue that contains received bytes until a byte is
available. */
if( xSerialGetChar( xPort, &cByteRxed, comRX_BLOCK_TIME ) )
{
/* Was this the byte we were expecting? If so, toggle the LED,
otherwise we are out on sync and should break out of the loop
until the expected character sequence is about to restart. */
if( cByteRxed == cExpectedByte )
{
vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET );
}
else
{
xResyncRequired = pdTRUE;
break; /*lint !e960 Non-switch break allowed. */
}
}
}
/* Turn the LED off while we are not doing anything. */
vParTestSetLED( uxBaseLED + comRX_LED_OFFSET, pdFALSE );
/* Did we break out of the loop because the characters were received in
an unexpected order? If so wait here until the character sequence is
about to restart. */
if( xResyncRequired == pdTRUE )
{
while( cByteRxed != comLAST_BYTE )
{
/* Block until the next char is available. */
xSerialGetChar( xPort, &cByteRxed, comRX_BLOCK_TIME );
}
/* Note that an error occurred which caused us to have to resync.
We use this to stop incrementing the loop counter so
sAreComTestTasksStillRunning() will return false - indicating an
error. */
xErrorOccurred++;
/* We have now resynced with the Tx task and can continue. */
xResyncRequired = pdFALSE;
}
else
{
if( xErrorOccurred < comTOTAL_PERMISSIBLE_ERRORS )
{
/* Increment the count of successful loops. As error
occurring (i.e. an unexpected character being received) will
prevent this counter being incremented for the rest of the
execution. Don't worry about mutual exclusion on this
variable - it doesn't really matter as we just want it
to change. */
uxRxLoops++;
}
}
}
} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */
/*-----------------------------------------------------------*/
BaseType_t xAreComTestTasksStillRunning( void )
{
BaseType_t xReturn;
/* If the count of successful reception loops has not changed than at
some time an error occurred (i.e. a character was received out of sequence)
and we will return false. */
if( uxRxLoops == comINITIAL_RX_COUNT_VALUE )
{
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
/* Reset the count of successful Rx loops. When this function is called
again we expect this to have been incremented. */
uxRxLoops = comINITIAL_RX_COUNT_VALUE;
return xReturn;
}

View File

@ -0,0 +1,311 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Creates a task and a timer that operate on an interrupt driven serial port.
* This demo assumes that the characters transmitted on a port will also be
* received on the same port. Therefore, the UART must either be connected to
* an echo server, or the uart connector must have a loopback connector fitted.
* See http://www.serialporttool.com/CommEcho.htm for a suitable echo server
* for Windows hosts.
*
* The timer sends a string to the UART, toggles an LED, then resets itself by
* changing its own period. The period is calculated as a pseudo random number
* between comTX_MAX_BLOCK_TIME and comTX_MIN_BLOCK_TIME.
*
* The task blocks on an Rx queue waiting for a character to become available.
* Received characters are checked to ensure they match those transmitted by the
* Tx timer. An error is latched if characters are missing, incorrect, or
* arrive too slowly.
*
* How characters are actually transmitted and received is port specific. Demos
* that include this test/demo file will provide example drivers. The Tx timer
* executes in the context of the timer service (daemon) task, and must
* therefore never attempt to block.
*
*/
/* Scheduler include files. */
#include <stdlib.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#ifndef configUSE_TIMERS
#error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.
#endif
#if configUSE_TIMERS != 1
#error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h.
#endif
/* Demo program include files. */
#include "serial.h"
#include "comtest_strings.h"
#include "partest.h"
/* The size of the stack given to the Rx task. */
#define comSTACK_SIZE configMINIMAL_STACK_SIZE
/* See the comment above the declaraction of the uxBaseLED variable. */
#define comTX_LED_OFFSET ( 0 )
#define comRX_LED_OFFSET ( 1 )
/* The Tx timer transmits the sequence of characters at a pseudo random
interval that is capped between comTX_MAX_BLOCK_TIME and
comTX_MIN_BLOCK_TIME. */
#define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x96 )
#define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0x32 )
#define comOFFSET_TIME ( ( TickType_t ) 3 )
/* States for the simple state machine implemented in the Rx task. */
#define comtstWAITING_START_OF_STRING 0
#define comtstWAITING_END_OF_STRING 1
/* A short delay in ticks - this delay is used to allow the Rx queue to fill up
a bit so more than one character can be processed at a time. This is relative
to comTX_MIN_BLOCK_TIME to ensure it is never longer than the shortest gap
between transmissions. It could be worked out more scientifically from the
baud rate being used. */
#define comSHORT_DELAY ( comTX_MIN_BLOCK_TIME >> ( TickType_t ) 2 )
/* The string that is transmitted and received. */
#define comTRANSACTED_STRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
/* A block time of 0 simply means "don't block". */
#define comtstDONT_BLOCK ( TickType_t ) 0
/* Handle to the com port used by both tasks. */
static xComPortHandle xPort = NULL;
/* The callback function allocated to the transmit timer, as described in the
comments at the top of this file. */
static void prvComTxTimerCallback( TimerHandle_t xTimer );
/* The receive task as described in the comments at the top of this file. */
static void vComRxTask( void *pvParameters );
/* The Rx task will toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task
will toggle LED ( uxBaseLED + comTX_LED_OFFSET ). */
static UBaseType_t uxBaseLED = 0;
/* The Rx task toggles uxRxLoops on each successful iteration of its defined
function - provided no errors have ever been latched. If this variable stops
incrementing, then an error has occurred. */
static volatile UBaseType_t uxRxLoops = 0UL;
/* The timer used to periodically transmit the string. This is the timer that
has prvComTxTimerCallback allocated to it as its callback function. */
static TimerHandle_t xTxTimer = NULL;
/* The string length is held at file scope so the Tx timer does not need to
calculate it each time it executes. */
static size_t xStringLength = 0U;
/*-----------------------------------------------------------*/
void vStartComTestStringsTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED )
{
/* Store values that are used at run time. */
uxBaseLED = uxLED;
/* Calculate the string length here, rather than each time the Tx timer
executes. */
xStringLength = strlen( comTRANSACTED_STRING );
/* Include the null terminator in the string length as this is used to
detect the end of the string in the Rx task. */
xStringLength++;
/* Initialise the com port, then spawn the Rx task and create the Tx
timer. */
xSerialPortInitMinimal( ulBaudRate, ( xStringLength * 2U ) );
/* Create the Rx task and the Tx timer. The timer is started from the
Rx task. */
xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL );
xTxTimer = xTimerCreate( "TxTimer", comTX_MIN_BLOCK_TIME, pdFALSE, NULL, prvComTxTimerCallback );
configASSERT( xTxTimer );
}
/*-----------------------------------------------------------*/
static void prvComTxTimerCallback( TimerHandle_t xTimer )
{
TickType_t xTimeToWait;
/* The parameter is not used in this case. */
( void ) xTimer;
/* Send the string. How this is actually performed depends on the
sample driver provided with this demo. However - as this is a timer,
it executes in the context of the timer task and therefore must not
block. */
vSerialPutString( xPort, comTRANSACTED_STRING, xStringLength );
/* Toggle an LED to give a visible indication that another transmission
has been performed. */
vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET );
/* Wait a pseudo random time before sending the string again. */
xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME;
/* Ensure the time to wait is not greater than comTX_MAX_BLOCK_TIME. */
xTimeToWait %= comTX_MAX_BLOCK_TIME;
/* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */
if( xTimeToWait < comTX_MIN_BLOCK_TIME )
{
xTimeToWait = comTX_MIN_BLOCK_TIME;
}
/* Reset the timer to run again xTimeToWait ticks from now. This function
is called from the context of the timer task, so the block time must not
be anything other than zero. */
xTimerChangePeriod( xTxTimer, xTimeToWait, comtstDONT_BLOCK );
}
/*-----------------------------------------------------------*/
static void vComRxTask( void *pvParameters )
{
BaseType_t xState = comtstWAITING_START_OF_STRING, xErrorOccurred = pdFALSE;
char *pcExpectedByte, cRxedChar;
const xComPortHandle xPort = NULL;
/* The parameter is not used in this example. */
( void ) pvParameters;
/* Start the Tx timer. This only needs to be started once, as it will
reset itself thereafter. */
xTimerStart( xTxTimer, portMAX_DELAY );
/* The first expected Rx character is the first in the string that is
transmitted. */
pcExpectedByte = comTRANSACTED_STRING;
for( ;; )
{
/* Wait for the next character. */
if( xSerialGetChar( xPort, &cRxedChar, ( comTX_MAX_BLOCK_TIME * 2 ) ) == pdFALSE )
{
/* A character definitely should have been received by now. As a
character was not received an error must have occurred (which might
just be that the loopback connector is not fitted). */
xErrorOccurred = pdTRUE;
}
switch( xState )
{
case comtstWAITING_START_OF_STRING:
if( cRxedChar == *pcExpectedByte )
{
/* The received character was the first character of the
string. Move to the next state to check each character
as it comes in until the entire string has been received. */
xState = comtstWAITING_END_OF_STRING;
pcExpectedByte++;
/* Block for a short period. This just allows the Rx queue
to contain more than one character, and therefore prevent
thrashing reads to the queue, and repetitive context
switches as each character is received. */
vTaskDelay( comSHORT_DELAY );
}
break;
case comtstWAITING_END_OF_STRING:
if( cRxedChar == *pcExpectedByte )
{
/* The received character was the expected character. Was
it the last character in the string - i.e. the null
terminator? */
if( cRxedChar == 0x00 )
{
/* The entire string has been received. If no errors
have been latched, then increment the loop counter to
show this task is still healthy. */
if( xErrorOccurred == pdFALSE )
{
uxRxLoops++;
/* Toggle an LED to give a visible sign that a
complete string has been received. */
vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET );
}
/* Go back to wait for the start of the next string. */
pcExpectedByte = comTRANSACTED_STRING;
xState = comtstWAITING_START_OF_STRING;
}
else
{
/* Wait for the next character in the string. */
pcExpectedByte++;
}
}
else
{
/* The character received was not that expected. */
xErrorOccurred = pdTRUE;
}
break;
default:
/* Should not get here. Stop the Rx loop counter from
incrementing to latch the error. */
xErrorOccurred = pdTRUE;
break;
}
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreComTestTasksStillRunning( void )
{
BaseType_t xReturn;
/* If the count of successful reception loops has not changed than at
some time an error occurred (i.e. a character was received out of sequence)
and false is returned. */
if( uxRxLoops == 0UL )
{
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
/* Reset the count of successful Rx loops. When this function is called
again it should have been incremented again. */
uxRxLoops = 0UL;
return xReturn;
}

View File

@ -0,0 +1,288 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Simple demonstration of the usage of counting semaphore.
*/
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo program include files. */
#include "countsem.h"
/* The maximum count value that the semaphore used for the demo can hold. */
#define countMAX_COUNT_VALUE ( 200 )
/* Constants used to indicate whether or not the semaphore should have been
created with its maximum count value, or its minimum count value. These
numbers are used to ensure that the pointers passed in as the task parameters
are valid. */
#define countSTART_AT_MAX_COUNT ( 0xaa )
#define countSTART_AT_ZERO ( 0x55 )
/* Two tasks are created for the test. One uses a semaphore created with its
count value set to the maximum, and one with the count value set to zero. */
#define countNUM_TEST_TASKS ( 2 )
#define countDONT_BLOCK ( 0 )
/*-----------------------------------------------------------*/
/* Flag that will be latched to pdTRUE should any unexpected behaviour be
detected in any of the tasks. */
static volatile BaseType_t xErrorDetected = pdFALSE;
/*-----------------------------------------------------------*/
/*
* The demo task. This simply counts the semaphore up to its maximum value,
* the counts it back down again. The result of each semaphore 'give' and
* 'take' is inspected, with an error being flagged if it is found not to be
* the expected result.
*/
static void prvCountingSemaphoreTask( void *pvParameters );
/*
* Utility function to increment the semaphore count value up from zero to
* countMAX_COUNT_VALUE.
*/
static void prvIncrementSemaphoreCount( SemaphoreHandle_t xSemaphore, volatile UBaseType_t *puxLoopCounter );
/*
* Utility function to decrement the semaphore count value up from
* countMAX_COUNT_VALUE to zero.
*/
static void prvDecrementSemaphoreCount( SemaphoreHandle_t xSemaphore, volatile UBaseType_t *puxLoopCounter );
/*-----------------------------------------------------------*/
/* The structure that is passed into the task as the task parameter. */
typedef struct COUNT_SEM_STRUCT
{
/* The semaphore to be used for the demo. */
SemaphoreHandle_t xSemaphore;
/* Set to countSTART_AT_MAX_COUNT if the semaphore should be created with
its count value set to its max count value, or countSTART_AT_ZERO if it
should have been created with its count value set to 0. */
UBaseType_t uxExpectedStartCount;
/* Incremented on each cycle of the demo task. Used to detect a stalled
task. */
volatile UBaseType_t uxLoopCounter;
} xCountSemStruct;
/* Two structures are defined, one is passed to each test task. */
static xCountSemStruct xParameters[ countNUM_TEST_TASKS ];
/*-----------------------------------------------------------*/
void vStartCountingSemaphoreTasks( void )
{
/* Create the semaphores that we are going to use for the test/demo. The
first should be created such that it starts at its maximum count value,
the second should be created such that it starts with a count value of zero. */
xParameters[ 0 ].xSemaphore = xSemaphoreCreateCounting( countMAX_COUNT_VALUE, countMAX_COUNT_VALUE );
xParameters[ 0 ].uxExpectedStartCount = countSTART_AT_MAX_COUNT;
xParameters[ 0 ].uxLoopCounter = 0;
xParameters[ 1 ].xSemaphore = xSemaphoreCreateCounting( countMAX_COUNT_VALUE, 0 );
xParameters[ 1 ].uxExpectedStartCount = 0;
xParameters[ 1 ].uxLoopCounter = 0;
/* Were the semaphores created? */
if( ( xParameters[ 0 ].xSemaphore != NULL ) || ( xParameters[ 1 ].xSemaphore != NULL ) )
{
/* vQueueAddToRegistry() adds the semaphore to the registry, if one is
in use. The registry is provided as a means for kernel aware
debuggers to locate semaphores and has no purpose if a kernel aware
debugger is not being used. The call to vQueueAddToRegistry() will be
removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
defined or is defined to be less than 1. */
vQueueAddToRegistry( ( QueueHandle_t ) xParameters[ 0 ].xSemaphore, "Counting_Sem_1" );
vQueueAddToRegistry( ( QueueHandle_t ) xParameters[ 1 ].xSemaphore, "Counting_Sem_2" );
/* Create the demo tasks, passing in the semaphore to use as the parameter. */
xTaskCreate( prvCountingSemaphoreTask, "CNT1", configMINIMAL_STACK_SIZE, ( void * ) &( xParameters[ 0 ] ), tskIDLE_PRIORITY, NULL );
xTaskCreate( prvCountingSemaphoreTask, "CNT2", configMINIMAL_STACK_SIZE, ( void * ) &( xParameters[ 1 ] ), tskIDLE_PRIORITY, NULL );
}
}
/*-----------------------------------------------------------*/
static void prvDecrementSemaphoreCount( SemaphoreHandle_t xSemaphore, volatile UBaseType_t *puxLoopCounter )
{
UBaseType_t ux;
/* If the semaphore count is at its maximum then we should not be able to
'give' the semaphore. */
if( xSemaphoreGive( xSemaphore ) == pdPASS )
{
xErrorDetected = pdTRUE;
}
/* We should be able to 'take' the semaphore countMAX_COUNT_VALUE times. */
for( ux = 0; ux < countMAX_COUNT_VALUE; ux++ )
{
configASSERT( uxSemaphoreGetCount( xSemaphore ) == ( countMAX_COUNT_VALUE - ux ) );
if( xSemaphoreTake( xSemaphore, countDONT_BLOCK ) != pdPASS )
{
/* We expected to be able to take the semaphore. */
xErrorDetected = pdTRUE;
}
( *puxLoopCounter )++;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* If the semaphore count is zero then we should not be able to 'take'
the semaphore. */
configASSERT( uxSemaphoreGetCount( xSemaphore ) == 0 );
if( xSemaphoreTake( xSemaphore, countDONT_BLOCK ) == pdPASS )
{
xErrorDetected = pdTRUE;
}
}
/*-----------------------------------------------------------*/
static void prvIncrementSemaphoreCount( SemaphoreHandle_t xSemaphore, volatile UBaseType_t *puxLoopCounter )
{
UBaseType_t ux;
/* If the semaphore count is zero then we should not be able to 'take'
the semaphore. */
if( xSemaphoreTake( xSemaphore, countDONT_BLOCK ) == pdPASS )
{
xErrorDetected = pdTRUE;
}
/* We should be able to 'give' the semaphore countMAX_COUNT_VALUE times. */
for( ux = 0; ux < countMAX_COUNT_VALUE; ux++ )
{
configASSERT( uxSemaphoreGetCount( xSemaphore ) == ux );
if( xSemaphoreGive( xSemaphore ) != pdPASS )
{
/* We expected to be able to take the semaphore. */
xErrorDetected = pdTRUE;
}
( *puxLoopCounter )++;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* If the semaphore count is at its maximum then we should not be able to
'give' the semaphore. */
if( xSemaphoreGive( xSemaphore ) == pdPASS )
{
xErrorDetected = pdTRUE;
}
}
/*-----------------------------------------------------------*/
static void prvCountingSemaphoreTask( void *pvParameters )
{
xCountSemStruct *pxParameter;
#ifdef USE_STDIO
void vPrintDisplayMessage( const char * const * ppcMessageToSend );
const char * const pcTaskStartMsg = "Counting semaphore demo started.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
#endif
/* The semaphore to be used was passed as the parameter. */
pxParameter = ( xCountSemStruct * ) pvParameters;
/* Did we expect to find the semaphore already at its max count value, or
at zero? */
if( pxParameter->uxExpectedStartCount == countSTART_AT_MAX_COUNT )
{
prvDecrementSemaphoreCount( pxParameter->xSemaphore, &( pxParameter->uxLoopCounter ) );
}
/* Now we expect the semaphore count to be 0, so this time there is an
error if we can take the semaphore. */
if( xSemaphoreTake( pxParameter->xSemaphore, 0 ) == pdPASS )
{
xErrorDetected = pdTRUE;
}
for( ;; )
{
prvIncrementSemaphoreCount( pxParameter->xSemaphore, &( pxParameter->uxLoopCounter ) );
prvDecrementSemaphoreCount( pxParameter->xSemaphore, &( pxParameter->uxLoopCounter ) );
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreCountingSemaphoreTasksStillRunning( void )
{
static UBaseType_t uxLastCount0 = 0, uxLastCount1 = 0;
BaseType_t xReturn = pdPASS;
/* Return fail if any 'give' or 'take' did not result in the expected
behaviour. */
if( xErrorDetected != pdFALSE )
{
xReturn = pdFAIL;
}
/* Return fail if either task is not still incrementing its loop counter. */
if( uxLastCount0 == xParameters[ 0 ].uxLoopCounter )
{
xReturn = pdFAIL;
}
else
{
uxLastCount0 = xParameters[ 0 ].uxLoopCounter;
}
if( uxLastCount1 == xParameters[ 1 ].uxLoopCounter )
{
xReturn = pdFAIL;
}
else
{
uxLastCount1 = xParameters[ 1 ].uxLoopCounter;
}
return xReturn;
}

View File

@ -0,0 +1,208 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This demo application file demonstrates the use of queues to pass data
* between co-routines.
*
* N represents the number of 'fixed delay' co-routines that are created and
* is set during initialisation.
*
* N 'fixed delay' co-routines are created that just block for a fixed
* period then post the number of an LED onto a queue. Each such co-routine
* uses a different block period. A single 'flash' co-routine is also created
* that blocks on the same queue, waiting for the number of the next LED it
* should flash. Upon receiving a number it simply toggle the instructed LED
* then blocks on the queue once more. In this manner each LED from LED 0 to
* LED N-1 is caused to flash at a different rate.
*
* The 'fixed delay' co-routines are created with co-routine priority 0. The
* flash co-routine is created with co-routine priority 1. This means that
* the queue should never contain more than a single item. This is because
* posting to the queue will unblock the 'flash' co-routine, and as this has
* a priority greater than the tasks posting to the queue it is guaranteed to
* have emptied the queue and blocked once again before the queue can contain
* any more date. An error is indicated if an attempt to post data to the
* queue fails - indicating that the queue is already full.
*
*/
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "croutine.h"
#include "queue.h"
/* Demo application includes. */
#include "partest.h"
#include "crflash.h"
/* The queue should only need to be of length 1. See the description at the
top of the file. */
#define crfQUEUE_LENGTH 1
#define crfFIXED_DELAY_PRIORITY 0
#define crfFLASH_PRIORITY 1
/* Only one flash co-routine is created so the index is not significant. */
#define crfFLASH_INDEX 0
/* Don't allow more than crfMAX_FLASH_TASKS 'fixed delay' co-routines to be
created. */
#define crfMAX_FLASH_TASKS 8
/* We don't want to block when posting to the queue. */
#define crfPOSTING_BLOCK_TIME 0
/*
* The 'fixed delay' co-routine as described at the top of the file.
*/
static void prvFixedDelayCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex );
/*
* The 'flash' co-routine as described at the top of the file.
*/
static void prvFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex );
/* The queue used to pass data between the 'fixed delay' co-routines and the
'flash' co-routine. */
static QueueHandle_t xFlashQueue;
/* This will be set to pdFALSE if we detect an error. */
static BaseType_t xCoRoutineFlashStatus = pdPASS;
/*-----------------------------------------------------------*/
/*
* See the header file for details.
*/
void vStartFlashCoRoutines( UBaseType_t uxNumberToCreate )
{
UBaseType_t uxIndex;
if( uxNumberToCreate > crfMAX_FLASH_TASKS )
{
uxNumberToCreate = crfMAX_FLASH_TASKS;
}
/* Create the queue used to pass data between the co-routines. */
xFlashQueue = xQueueCreate( crfQUEUE_LENGTH, sizeof( UBaseType_t ) );
if( xFlashQueue )
{
/* Create uxNumberToCreate 'fixed delay' co-routines. */
for( uxIndex = 0; uxIndex < uxNumberToCreate; uxIndex++ )
{
xCoRoutineCreate( prvFixedDelayCoRoutine, crfFIXED_DELAY_PRIORITY, uxIndex );
}
/* Create the 'flash' co-routine. */
xCoRoutineCreate( prvFlashCoRoutine, crfFLASH_PRIORITY, crfFLASH_INDEX );
}
}
/*-----------------------------------------------------------*/
static void prvFixedDelayCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
{
/* Even though this is a co-routine the xResult variable does not need to be
static as we do not need it to maintain its state between blocks. */
BaseType_t xResult;
/* The uxIndex parameter of the co-routine function is used as an index into
the xFlashRates array to obtain the delay period to use. */
static const TickType_t xFlashRates[ crfMAX_FLASH_TASKS ] = { 150 / portTICK_PERIOD_MS,
200 / portTICK_PERIOD_MS,
250 / portTICK_PERIOD_MS,
300 / portTICK_PERIOD_MS,
350 / portTICK_PERIOD_MS,
400 / portTICK_PERIOD_MS,
450 / portTICK_PERIOD_MS,
500 / portTICK_PERIOD_MS };
/* Co-routines MUST start with a call to crSTART. */
crSTART( xHandle );
for( ;; )
{
/* Post our uxIndex value onto the queue. This is used as the LED to
flash. */
crQUEUE_SEND( xHandle, xFlashQueue, ( void * ) &uxIndex, crfPOSTING_BLOCK_TIME, &xResult );
if( xResult != pdPASS )
{
/* For the reasons stated at the top of the file we should always
find that we can post to the queue. If we could not then an error
has occurred. */
xCoRoutineFlashStatus = pdFAIL;
}
crDELAY( xHandle, xFlashRates[ uxIndex ] );
}
/* Co-routines MUST end with a call to crEND. */
crEND();
}
/*-----------------------------------------------------------*/
static void prvFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
{
/* Even though this is a co-routine the variable do not need to be
static as we do not need it to maintain their state between blocks. */
BaseType_t xResult;
UBaseType_t uxLEDToFlash;
/* Co-routines MUST start with a call to crSTART. */
crSTART( xHandle );
( void ) uxIndex;
for( ;; )
{
/* Block to wait for the number of the LED to flash. */
crQUEUE_RECEIVE( xHandle, xFlashQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
if( xResult != pdPASS )
{
/* We would not expect to wake unless we received something. */
xCoRoutineFlashStatus = pdFAIL;
}
else
{
/* We received the number of an LED to flash - flash it! */
vParTestToggleLED( uxLEDToFlash );
}
}
/* Co-routines MUST end with a call to crEND. */
crEND();
}
/*-----------------------------------------------------------*/
BaseType_t xAreFlashCoRoutinesStillRunning( void )
{
/* Return pdPASS or pdFAIL depending on whether an error has been detected
or not. */
return xCoRoutineFlashStatus;
}

View File

@ -0,0 +1,232 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* This demo file demonstrates how to send data between an ISR and a
* co-routine. A tick hook function is used to periodically pass data between
* the RTOS tick and a set of 'hook' co-routines.
*
* hookNUM_HOOK_CO_ROUTINES co-routines are created. Each co-routine blocks
* to wait for a character to be received on a queue from the tick ISR, checks
* to ensure the character received was that expected, then sends the number
* back to the tick ISR on a different queue.
*
* The tick ISR checks the numbers received back from the 'hook' co-routines
* matches the number previously sent.
*
* If at any time a queue function returns unexpectedly, or an incorrect value
* is received either by the tick hook or a co-routine then an error is
* latched.
*
* This demo relies on each 'hook' co-routine to execute between each
* hookTICK_CALLS_BEFORE_POST tick interrupts. This and the heavy use of
* queues from within an interrupt may result in an error being detected on
* slower targets simply due to timing.
*/
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "croutine.h"
#include "queue.h"
/* Demo application includes. */
#include "crhook.h"
/* The number of 'hook' co-routines that are to be created. */
#define hookNUM_HOOK_CO_ROUTINES ( 4 )
/* The number of times the tick hook should be called before a character is
posted to the 'hook' co-routines. */
#define hookTICK_CALLS_BEFORE_POST ( 500 )
/* There should never be more than one item in any queue at any time. */
#define hookHOOK_QUEUE_LENGTH ( 1 )
/* Don't block when initially posting to the queue. */
#define hookNO_BLOCK_TIME ( 0 )
/* The priority relative to other co-routines (rather than tasks) that the
'hook' co-routines should take. */
#define mainHOOK_CR_PRIORITY ( 1 )
/*-----------------------------------------------------------*/
/*
* The co-routine function itself.
*/
static void prvHookCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex );
/*
* The tick hook function. This receives a number from each 'hook' co-routine
* then sends a number to each co-routine. An error is flagged if a send or
* receive fails, or an unexpected number is received.
*/
void vApplicationTickHook( void );
/*-----------------------------------------------------------*/
/* Queues used to send data FROM a co-routine TO the tick hook function.
The hook functions received (Rx's) on these queues. One queue per
'hook' co-routine. */
static QueueHandle_t xHookRxQueues[ hookNUM_HOOK_CO_ROUTINES ];
/* Queues used to send data FROM the tick hook TO a co-routine function.
The hood function transmits (Tx's) on these queues. One queue per
'hook' co-routine. */
static QueueHandle_t xHookTxQueues[ hookNUM_HOOK_CO_ROUTINES ];
/* Set to true if an error is detected at any time. */
static BaseType_t xCoRoutineErrorDetected = pdFALSE;
/*-----------------------------------------------------------*/
void vStartHookCoRoutines( void )
{
UBaseType_t uxIndex, uxValueToPost = 0;
for( uxIndex = 0; uxIndex < hookNUM_HOOK_CO_ROUTINES; uxIndex++ )
{
/* Create a queue to transmit to and receive from each 'hook'
co-routine. */
xHookRxQueues[ uxIndex ] = xQueueCreate( hookHOOK_QUEUE_LENGTH, sizeof( UBaseType_t ) );
xHookTxQueues[ uxIndex ] = xQueueCreate( hookHOOK_QUEUE_LENGTH, sizeof( UBaseType_t ) );
/* To start things off the tick hook function expects the queue it
uses to receive data to contain a value. */
xQueueSend( xHookRxQueues[ uxIndex ], &uxValueToPost, hookNO_BLOCK_TIME );
/* Create the 'hook' co-routine itself. */
xCoRoutineCreate( prvHookCoRoutine, mainHOOK_CR_PRIORITY, uxIndex );
}
}
/*-----------------------------------------------------------*/
static UBaseType_t uxCallCounter = 0, uxNumberToPost = 0;
void vApplicationTickHook( void )
{
UBaseType_t uxReceivedNumber;
BaseType_t xIndex, xCoRoutineWoken;
/* Is it time to talk to the 'hook' co-routines again? */
uxCallCounter++;
if( uxCallCounter >= hookTICK_CALLS_BEFORE_POST )
{
uxCallCounter = 0;
for( xIndex = 0; xIndex < hookNUM_HOOK_CO_ROUTINES; xIndex++ )
{
xCoRoutineWoken = pdFALSE;
if( crQUEUE_RECEIVE_FROM_ISR( xHookRxQueues[ xIndex ], &uxReceivedNumber, &xCoRoutineWoken ) != pdPASS )
{
/* There is no reason why we would not expect the queue to
contain a value. */
xCoRoutineErrorDetected = pdTRUE;
}
else
{
/* Each queue used to receive data from the 'hook' co-routines
should contain the number we last posted to the same co-routine. */
if( uxReceivedNumber != uxNumberToPost )
{
xCoRoutineErrorDetected = pdTRUE;
}
/* Nothing should be blocked waiting to post to the queue. */
if( xCoRoutineWoken != pdFALSE )
{
xCoRoutineErrorDetected = pdTRUE;
}
}
}
/* Start the next cycle by posting the next number onto each Tx queue. */
uxNumberToPost++;
for( xIndex = 0; xIndex < hookNUM_HOOK_CO_ROUTINES; xIndex++ )
{
if( crQUEUE_SEND_FROM_ISR( xHookTxQueues[ xIndex ], &uxNumberToPost, pdFALSE ) != pdTRUE )
{
/* Posting to the queue should have woken the co-routine that
was blocked on the queue. */
xCoRoutineErrorDetected = pdTRUE;
}
}
}
}
/*-----------------------------------------------------------*/
static void prvHookCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
{
static UBaseType_t uxReceivedValue[ hookNUM_HOOK_CO_ROUTINES ];
BaseType_t xResult;
/* Each co-routine MUST start with a call to crSTART(); */
crSTART( xHandle );
for( ;; )
{
/* Wait to receive a value from the tick hook. */
xResult = pdFAIL;
crQUEUE_RECEIVE( xHandle, xHookTxQueues[ uxIndex ], &( uxReceivedValue[ uxIndex ] ), portMAX_DELAY, &xResult );
/* There is no reason why we should not have received something on
the queue. */
if( xResult != pdPASS )
{
xCoRoutineErrorDetected = pdTRUE;
}
/* Send the same number back to the idle hook so it can verify it. */
xResult = pdFAIL;
crQUEUE_SEND( xHandle, xHookRxQueues[ uxIndex ], &( uxReceivedValue[ uxIndex ] ), hookNO_BLOCK_TIME, &xResult );
if( xResult != pdPASS )
{
/* There is no reason why we should not have been able to post to
the queue. */
xCoRoutineErrorDetected = pdTRUE;
}
}
/* Each co-routine MUST end with a call to crEND(). */
crEND();
}
/*-----------------------------------------------------------*/
BaseType_t xAreHookCoRoutinesStillRunning( void )
{
if( xCoRoutineErrorDetected )
{
return pdFALSE;
}
else
{
return pdTRUE;
}
}

View File

@ -0,0 +1,203 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Create a single persistent task which periodically dynamically creates another
* two tasks. The original task is called the creator task, the two tasks it
* creates are called suicidal tasks.
*
* One of the created suicidal tasks kill one other suicidal task before killing
* itself - leaving just the original task remaining.
*
* The creator task must be spawned after all of the other demo application tasks
* as it keeps a check on the number of tasks under the scheduler control. The
* number of tasks it expects to see running should never be greater than the
* number of tasks that were in existence when the creator task was spawned, plus
* one set of four suicidal tasks. If this number is exceeded an error is flagged.
*
* \page DeathC death.c
* \ingroup DemoFiles
* <HR>
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "death.h"
#define deathSTACK_SIZE ( configMINIMAL_STACK_SIZE + 60 )
/* The task originally created which is responsible for periodically dynamically
creating another four tasks. */
static portTASK_FUNCTION_PROTO( vCreateTasks, pvParameters );
/* The task function of the dynamically created tasks. */
static portTASK_FUNCTION_PROTO( vSuicidalTask, pvParameters );
/* A variable which is incremented every time the dynamic tasks are created. This
is used to check that the task is still running. */
static volatile uint16_t usCreationCount = 0;
/* Used to store the number of tasks that were originally running so the creator
task can tell if any of the suicidal tasks have failed to die.
*/
static volatile UBaseType_t uxTasksRunningAtStart = 0;
/* When a task deletes itself, it stack and TCB are cleaned up by the Idle task.
Under heavy load the idle task might not get much processing time, so it would
be legitimate for several tasks to remain undeleted for a short period. There
may also be a few other unexpected tasks if, for example, the tasks that test
static allocation are also being used. */
static const UBaseType_t uxMaxNumberOfExtraTasksRunning = 3;
/* Used to store a handle to the task that should be killed by a suicidal task,
before it kills itself. */
TaskHandle_t xCreatedTask;
/*-----------------------------------------------------------*/
void vCreateSuicidalTasks( UBaseType_t uxPriority )
{
xTaskCreate( vCreateTasks, "CREATOR", deathSTACK_SIZE, ( void * ) NULL, uxPriority, NULL );
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vSuicidalTask, pvParameters )
{
volatile long l1, l2;
TaskHandle_t xTaskToKill;
const TickType_t xDelay = pdMS_TO_TICKS( ( TickType_t ) 200 );
/* Test deletion of a task's secure context, if any. */
portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE );
if( pvParameters != NULL )
{
/* This task is periodically created four times. Two created tasks are
passed a handle to the other task so it can kill it before killing itself.
The other task is passed in null. */
xTaskToKill = *( TaskHandle_t* )pvParameters;
}
else
{
xTaskToKill = NULL;
}
for( ;; )
{
/* Do something random just to use some stack and registers. */
l1 = 2;
l2 = 89;
l2 *= l1;
vTaskDelay( xDelay );
if( xTaskToKill != NULL )
{
/* Make sure the other task has a go before we delete it. */
vTaskDelay( ( TickType_t ) 0 );
/* Kill the other task that was created by vCreateTasks(). */
vTaskDelete( xTaskToKill );
/* Kill ourselves. */
vTaskDelete( NULL );
}
}
}/*lint !e818 !e550 Function prototype must be as per standard for task functions. */
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCreateTasks, pvParameters )
{
const TickType_t xDelay = pdMS_TO_TICKS( ( TickType_t ) 1000 );
UBaseType_t uxPriority;
/* Remove compiler warning about unused parameter. */
( void ) pvParameters;
/* Delay at the start to ensure tasks created by other demos have been
created before storing the current number of tasks. */
vTaskDelay( xDelay );
uxTasksRunningAtStart = ( UBaseType_t ) uxTaskGetNumberOfTasks();
uxPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* Just loop round, delaying then creating the four suicidal tasks. */
vTaskDelay( xDelay );
xCreatedTask = NULL;
xTaskCreate( vSuicidalTask, "SUICID1", configMINIMAL_STACK_SIZE, NULL, uxPriority, &xCreatedTask );
xTaskCreate( vSuicidalTask, "SUICID2", configMINIMAL_STACK_SIZE, &xCreatedTask, uxPriority, NULL );
++usCreationCount;
}
}
/*-----------------------------------------------------------*/
/* This is called to check that the creator task is still running and that there
are not any more than four extra tasks. */
BaseType_t xIsCreateTaskStillRunning( void )
{
static uint16_t usLastCreationCount = 0xfff;
BaseType_t xReturn = pdTRUE;
static UBaseType_t uxTasksRunningNow;
if( usLastCreationCount == usCreationCount )
{
xReturn = pdFALSE;
}
else
{
usLastCreationCount = usCreationCount;
}
uxTasksRunningNow = ( UBaseType_t ) uxTaskGetNumberOfTasks();
if( uxTasksRunningNow < uxTasksRunningAtStart )
{
xReturn = pdFALSE;
}
else if( ( uxTasksRunningNow - uxTasksRunningAtStart ) > uxMaxNumberOfExtraTasksRunning )
{
xReturn = pdFALSE;
}
else
{
/* Everything is okay. */
}
return xReturn;
}

View File

@ -0,0 +1,479 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* The first test creates three tasks - two counter tasks (one continuous count
* and one limited count) and one controller. A "count" variable is shared
* between all three tasks. The two counter tasks should never be in a "ready"
* state at the same time. The controller task runs at the same priority as
* the continuous count task, and at a lower priority than the limited count
* task.
*
* One counter task loops indefinitely, incrementing the shared count variable
* on each iteration. To ensure it has exclusive access to the variable it
* raises its priority above that of the controller task before each
* increment, lowering it again to its original priority before starting the
* next iteration.
*
* The other counter task increments the shared count variable on each
* iteration of its loop until the count has reached a limit of 0xff - at
* which point it suspends itself. It will not start a new loop until the
* controller task has made it "ready" again by calling vTaskResume().
* This second counter task operates at a higher priority than controller
* task so does not need to worry about mutual exclusion of the counter
* variable.
*
* The controller task is in two sections. The first section controls and
* monitors the continuous count task. When this section is operational the
* limited count task is suspended. Likewise, the second section controls
* and monitors the limited count task. When this section is operational the
* continuous count task is suspended.
*
* In the first section the controller task first takes a copy of the shared
* count variable. To ensure mutual exclusion on the count variable it
* suspends the continuous count task, resuming it again when the copy has been
* taken. The controller task then sleeps for a fixed period - during which
* the continuous count task will execute and increment the shared variable.
* When the controller task wakes it checks that the continuous count task
* has executed by comparing the copy of the shared variable with its current
* value. This time, to ensure mutual exclusion, the scheduler itself is
* suspended with a call to vTaskSuspendAll (). This is for demonstration
* purposes only and is not a recommended technique due to its inefficiency.
*
* After a fixed number of iterations the controller task suspends the
* continuous count task, and moves on to its second section.
*
* At the start of the second section the shared variable is cleared to zero.
* The limited count task is then woken from its suspension by a call to
* vTaskResume (). As this counter task operates at a higher priority than
* the controller task the controller task should not run again until the
* shared variable has been counted up to the limited value causing the counter
* task to suspend itself. The next line after vTaskResume () is therefore
* a check on the shared variable to ensure everything is as expected.
*
*
* The second test consists of a couple of very simple tasks that post onto a
* queue while the scheduler is suspended. This test was added to test parts
* of the scheduler not exercised by the first test.
*
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo app include files. */
#include "dynamic.h"
/* Function that implements the "limited count" task as described above. */
static portTASK_FUNCTION_PROTO( vLimitedIncrementTask, pvParameters );
/* Function that implements the "continuous count" task as described above. */
static portTASK_FUNCTION_PROTO( vContinuousIncrementTask, pvParameters );
/* Function that implements the controller task as described above. */
static portTASK_FUNCTION_PROTO( vCounterControlTask, pvParameters );
static portTASK_FUNCTION_PROTO( vQueueReceiveWhenSuspendedTask, pvParameters );
static portTASK_FUNCTION_PROTO( vQueueSendWhenSuspendedTask, pvParameters );
/* Demo task specific constants. */
#ifndef priSUSPENDED_RX_TASK_STACK_SIZE
#define priSUSPENDED_RX_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE )
#endif
#define priSTACK_SIZE ( configMINIMAL_STACK_SIZE )
#define priSLEEP_TIME pdMS_TO_TICKS( 128 )
#define priLOOPS ( 5 )
#define priMAX_COUNT ( ( uint32_t ) 0xff )
#define priNO_BLOCK ( ( TickType_t ) 0 )
#define priSUSPENDED_QUEUE_LENGTH ( 1 )
/*-----------------------------------------------------------*/
/* Handles to the two counter tasks. These could be passed in as parameters
to the controller task to prevent them having to be file scope. */
static TaskHandle_t xContinuousIncrementHandle, xLimitedIncrementHandle;
/* The shared counter variable. This is passed in as a parameter to the two
counter variables for demonstration purposes. */
static uint32_t ulCounter;
/* Variables used to check that the tasks are still operating without error.
Each complete iteration of the controller task increments this variable
provided no errors have been found. The variable maintaining the same value
is therefore indication of an error. */
static volatile uint16_t usCheckVariable = ( uint16_t ) 0;
static volatile BaseType_t xSuspendedQueueSendError = pdFALSE;
static volatile BaseType_t xSuspendedQueueReceiveError = pdFALSE;
/* Queue used by the second test. */
QueueHandle_t xSuspendedTestQueue;
/* The value the queue receive task expects to receive next. This is file
scope so xAreDynamicPriorityTasksStillRunning() can ensure it is still
incrementing. */
static uint32_t ulExpectedValue = ( uint32_t ) 0;
/*-----------------------------------------------------------*/
/*
* Start the three tasks as described at the top of the file.
* Note that the limited count task is given a higher priority.
*/
void vStartDynamicPriorityTasks( void )
{
xSuspendedTestQueue = xQueueCreate( priSUSPENDED_QUEUE_LENGTH, sizeof( uint32_t ) );
if( xSuspendedTestQueue != NULL )
{
/* vQueueAddToRegistry() adds the queue to the queue registry, if one is
in use. The queue registry is provided as a means for kernel aware
debuggers to locate queues and has no purpose if a kernel aware debugger
is not being used. The call to vQueueAddToRegistry() will be removed
by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
defined to be less than 1. */
vQueueAddToRegistry( xSuspendedTestQueue, "Suspended_Test_Queue" );
xTaskCreate( vContinuousIncrementTask, "CNT_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY, &xContinuousIncrementHandle );
xTaskCreate( vLimitedIncrementTask, "LIM_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY + 1, &xLimitedIncrementHandle );
xTaskCreate( vCounterControlTask, "C_CTRL", priSUSPENDED_RX_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
xTaskCreate( vQueueSendWhenSuspendedTask, "SUSP_TX", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
xTaskCreate( vQueueReceiveWhenSuspendedTask, "SUSP_RX", priSUSPENDED_RX_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
}
}
/*-----------------------------------------------------------*/
/*
* Just loops around incrementing the shared variable until the limit has been
* reached. Once the limit has been reached it suspends itself.
*/
static portTASK_FUNCTION( vLimitedIncrementTask, pvParameters )
{
volatile uint32_t *pulCounter;
/* Take a pointer to the shared variable from the parameters passed into
the task. */
pulCounter = ( volatile uint32_t * ) pvParameters;
/* This will run before the control task, so the first thing it does is
suspend - the control task will resume it when ready. */
vTaskSuspend( NULL );
for( ;; )
{
/* Just count up to a value then suspend. */
( *pulCounter )++;
if( *pulCounter >= priMAX_COUNT )
{
vTaskSuspend( NULL );
}
}
}
/*-----------------------------------------------------------*/
/*
* Just keep counting the shared variable up. The control task will suspend
* this task when it wants.
*/
static portTASK_FUNCTION( vContinuousIncrementTask, pvParameters )
{
volatile uint32_t *pulCounter;
UBaseType_t uxOurPriority;
/* Take a pointer to the shared variable from the parameters passed into
the task. */
pulCounter = ( volatile uint32_t * ) pvParameters;
/* Query our priority so we can raise it when exclusive access to the
shared variable is required. */
uxOurPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* Raise the priority above the controller task to ensure a context
switch does not occur while the variable is being accessed. */
vTaskPrioritySet( NULL, uxOurPriority + 1 );
{
configASSERT( ( uxTaskPriorityGet( NULL ) == ( uxOurPriority + 1 ) ) );
( *pulCounter )++;
}
vTaskPrioritySet( NULL, uxOurPriority );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
configASSERT( ( uxTaskPriorityGet( NULL ) == uxOurPriority ) );
}
}
/*-----------------------------------------------------------*/
/*
* Controller task as described above.
*/
static portTASK_FUNCTION( vCounterControlTask, pvParameters )
{
uint32_t ulLastCounter;
short sLoops;
short sError = pdFALSE;
/* Just to stop warning messages. */
( void ) pvParameters;
for( ;; )
{
/* Start with the counter at zero. */
ulCounter = ( uint32_t ) 0;
/* First section : */
/* Check the continuous count task is running. */
for( sLoops = 0; sLoops < priLOOPS; sLoops++ )
{
/* Suspend the continuous count task so we can take a mirror of the
shared variable without risk of corruption. This is not really
needed as the other task raises its priority above this task's
priority. */
vTaskSuspend( xContinuousIncrementHandle );
{
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xContinuousIncrementHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
ulLastCounter = ulCounter;
}
vTaskResume( xContinuousIncrementHandle );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xContinuousIncrementHandle ) == eReady );
}
#endif /* INCLUDE_eTaskGetState */
/* Now delay to ensure the other task has processor time. */
vTaskDelay( priSLEEP_TIME );
/* Check the shared variable again. This time to ensure mutual
exclusion the whole scheduler will be locked. This is just for
demo purposes! */
vTaskSuspendAll();
{
if( ulLastCounter == ulCounter )
{
/* The shared variable has not changed. There is a problem
with the continuous count task so flag an error. */
sError = pdTRUE;
}
}
xTaskResumeAll();
}
/* Second section: */
/* Suspend the continuous counter task so it stops accessing the shared
variable. */
vTaskSuspend( xContinuousIncrementHandle );
/* Reset the variable. */
ulCounter = ( uint32_t ) 0;
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xLimitedIncrementHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
/* Resume the limited count task which has a higher priority than us.
We should therefore not return from this call until the limited count
task has suspended itself with a known value in the counter variable. */
vTaskResume( xLimitedIncrementHandle );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
/* This task should not run again until xLimitedIncrementHandle has
suspended itself. */
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xLimitedIncrementHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
/* Does the counter variable have the expected value? */
if( ulCounter != priMAX_COUNT )
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If no errors have occurred then increment the check variable. */
portENTER_CRITICAL();
usCheckVariable++;
portEXIT_CRITICAL();
}
/* Resume the continuous count task and do it all again. */
vTaskResume( xContinuousIncrementHandle );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vQueueSendWhenSuspendedTask, pvParameters )
{
static uint32_t ulValueToSend = ( uint32_t ) 0;
/* Just to stop warning messages. */
( void ) pvParameters;
for( ;; )
{
vTaskSuspendAll();
{
/* We must not block while the scheduler is suspended! */
if( xQueueSend( xSuspendedTestQueue, ( void * ) &ulValueToSend, priNO_BLOCK ) != pdTRUE )
{
xSuspendedQueueSendError = pdTRUE;
}
}
xTaskResumeAll();
vTaskDelay( priSLEEP_TIME );
++ulValueToSend;
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vQueueReceiveWhenSuspendedTask, pvParameters )
{
uint32_t ulReceivedValue;
BaseType_t xGotValue;
/* Just to stop warning messages. */
( void ) pvParameters;
for( ;; )
{
do
{
/* Suspending the scheduler here is fairly pointless and
undesirable for a normal application. It is done here purely
to test the scheduler. The inner xTaskResumeAll() should
never return pdTRUE as the scheduler is still locked by the
outer call. */
vTaskSuspendAll();
{
vTaskSuspendAll();
{
xGotValue = xQueueReceive( xSuspendedTestQueue, ( void * ) &ulReceivedValue, priNO_BLOCK );
}
if( xTaskResumeAll() != pdFALSE )
{
xSuspendedQueueReceiveError = pdTRUE;
}
}
xTaskResumeAll();
#if configUSE_PREEMPTION == 0
{
taskYIELD();
}
#endif
} while( xGotValue == pdFALSE );
if( ulReceivedValue != ulExpectedValue )
{
xSuspendedQueueReceiveError = pdTRUE;
}
if( xSuspendedQueueReceiveError != pdTRUE )
{
/* Only increment the variable if an error has not occurred. This
allows xAreDynamicPriorityTasksStillRunning() to check for stalled
tasks as well as explicit errors. */
++ulExpectedValue;
}
}
}
/*-----------------------------------------------------------*/
/* Called to check that all the created tasks are still running without error. */
BaseType_t xAreDynamicPriorityTasksStillRunning( void )
{
/* Keep a history of the check variables so we know if it has been incremented
since the last call. */
static uint16_t usLastTaskCheck = ( uint16_t ) 0;
static uint32_t ulLastExpectedValue = ( uint32_t ) 0U;
BaseType_t xReturn = pdTRUE;
/* Check the tasks are still running by ensuring the check variable
is still incrementing. */
if( usCheckVariable == usLastTaskCheck )
{
/* The check has not incremented so an error exists. */
xReturn = pdFALSE;
}
if( ulExpectedValue == ulLastExpectedValue )
{
/* The value being received by the queue receive task has not
incremented so an error exists. */
xReturn = pdFALSE;
}
if( xSuspendedQueueSendError == pdTRUE )
{
xReturn = pdFALSE;
}
if( xSuspendedQueueReceiveError == pdTRUE )
{
xReturn = pdFALSE;
}
usLastTaskCheck = usCheckVariable;
ulLastExpectedValue = ulExpectedValue;
return xReturn;
}

View File

@ -0,0 +1,119 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* This version of flash .c is for use on systems that have limited stack space
* and no display facilities. The complete version can be found in the
* Demo/Common/Full directory.
*
* Three tasks are created, each of which flash an LED at a different rate. The first
* LED flashes every 200ms, the second every 400ms, the third every 600ms.
*
* The LED flash tasks provide instant visual feedback. They show that the scheduler
* is still operational.
*
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "partest.h"
#include "flash.h"
#define ledSTACK_SIZE configMINIMAL_STACK_SIZE
#define ledNUMBER_OF_LEDS ( 3 )
#define ledFLASH_RATE_BASE ( ( TickType_t ) 333 )
/* Variable used by the created tasks to calculate the LED number to use, and
the rate at which they should flash the LED. */
static volatile UBaseType_t uxFlashTaskNumber = 0;
/* The task that is created three times. */
static portTASK_FUNCTION_PROTO( vLEDFlashTask, pvParameters );
/*-----------------------------------------------------------*/
void vStartLEDFlashTasks( UBaseType_t uxPriority )
{
BaseType_t xLEDTask;
/* Create the three tasks. */
for( xLEDTask = 0; xLEDTask < ledNUMBER_OF_LEDS; ++xLEDTask )
{
/* Spawn the task. */
xTaskCreate( vLEDFlashTask, "LEDx", ledSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL );
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vLEDFlashTask, pvParameters )
{
TickType_t xFlashRate, xLastFlashTime;
UBaseType_t uxLED;
/* The parameters are not used. */
( void ) pvParameters;
/* Calculate the LED and flash rate. */
portENTER_CRITICAL();
{
/* See which of the eight LED's we should use. */
uxLED = uxFlashTaskNumber;
/* Update so the next task uses the next LED. */
uxFlashTaskNumber++;
}
portEXIT_CRITICAL();
xFlashRate = ledFLASH_RATE_BASE + ( ledFLASH_RATE_BASE * ( TickType_t ) uxLED );
xFlashRate /= portTICK_PERIOD_MS;
/* We will turn the LED on and off again in the delay period, so each
delay is only half the total period. */
xFlashRate /= ( TickType_t ) 2;
/* We need to initialise xLastFlashTime prior to the first call to
vTaskDelayUntil(). */
xLastFlashTime = xTaskGetTickCount();
for(;;)
{
/* Delay for half the flash period then turn the LED on. */
vTaskDelayUntil( &xLastFlashTime, xFlashRate );
vParTestToggleLED( uxLED );
/* Delay for half the flash period then turn the LED off. */
vTaskDelayUntil( &xLastFlashTime, xFlashRate );
vParTestToggleLED( uxLED );
}
} /*lint !e715 !e818 !e830 Function definition must be standard for task creation. */

View File

@ -0,0 +1,98 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* Repeatedly toggles one or more LEDs using software timers - one timer per
* LED.
*/
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "timers.h"
/* Demo program include files. */
#include "partest.h"
#include "flash_timer.h"
/* The toggle rates are all a multple of ledFLASH_RATE_BASE. */
#define ledFLASH_RATE_BASE ( ( ( TickType_t ) 333 ) / portTICK_PERIOD_MS )
/* A block time of zero simple means "don't block". */
#define ledDONT_BLOCK ( ( TickType_t ) 0 )
/*-----------------------------------------------------------*/
/*
* The callback function used by each LED flashing timer. All the timers use
* this function, and the timer ID is used within the function to determine
* which timer has actually expired.
*/
static void prvLEDTimerCallback( TimerHandle_t xTimer );
/*-----------------------------------------------------------*/
void vStartLEDFlashTimers( UBaseType_t uxNumberOfLEDs )
{
UBaseType_t uxLEDTimer;
TimerHandle_t xTimer;
/* Create and start the requested number of timers. */
for( uxLEDTimer = 0; uxLEDTimer < uxNumberOfLEDs; ++uxLEDTimer )
{
/* Create the timer. */
xTimer = xTimerCreate( "Flasher", /* A text name, purely to help debugging. */
ledFLASH_RATE_BASE * ( uxLEDTimer + 1 ),/* The timer period, which is a multiple of ledFLASH_RATE_BASE. */
pdTRUE, /* This is an auto-reload timer, so xAutoReload is set to pdTRUE. */
( void * ) uxLEDTimer, /* The ID is used to identify the timer within the timer callback function, as each timer uses the same callback. */
prvLEDTimerCallback /* Each timer uses the same callback. */
);
/* If the timer was created successfully, attempt to start it. If the
scheduler has not yet been started then the timer command queue must
be long enough to hold each command sent to it until such time that the
scheduler is started. The timer command queue length is set by
configTIMER_QUEUE_LENGTH in FreeRTOSConfig.h. */
if( xTimer != NULL )
{
xTimerStart( xTimer, ledDONT_BLOCK );
}
}
}
/*-----------------------------------------------------------*/
static void prvLEDTimerCallback( TimerHandle_t xTimer )
{
BaseType_t xTimerID;
/* The timer ID is used to identify the timer that has actually expired as
each timer uses the same callback. The ID is then also used as the number
of the LED that is to be toggled. */
xTimerID = ( BaseType_t ) pvTimerGetTimerID( xTimer );
vParTestToggleLED( xTimerID );
}

View File

@ -0,0 +1,349 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Creates eight tasks, each of which loops continuously performing a floating
* point calculation.
*
* All the tasks run at the idle priority and never block or yield. This causes
* all eight tasks to time slice with the idle task. Running at the idle
* priority means that these tasks will get pre-empted any time another task is
* ready to run or a time slice occurs. More often than not the pre-emption
* will occur mid calculation, creating a good test of the schedulers context
* switch mechanism - a calculation producing an unexpected result could be a
* symptom of a corruption in the context of a task.
*/
/* Standard includes. */
#include <stdlib.h>
#include <math.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "flop.h"
#ifndef mathSTACK_SIZE
#define mathSTACK_SIZE configMINIMAL_STACK_SIZE
#endif
#define mathNUMBER_OF_TASKS ( 4 )
/* Four tasks, each of which performs a different floating point calculation.
Each of the four is created twice. */
static portTASK_FUNCTION_PROTO( vCompetingMathTask1, pvParameters );
static portTASK_FUNCTION_PROTO( vCompetingMathTask2, pvParameters );
static portTASK_FUNCTION_PROTO( vCompetingMathTask3, pvParameters );
static portTASK_FUNCTION_PROTO( vCompetingMathTask4, pvParameters );
/* These variables are used to check that all the tasks are still running. If a
task gets a calculation wrong it will stop setting its check variable. */
static uint16_t usTaskCheck[ mathNUMBER_OF_TASKS ] = { ( uint16_t ) 0 };
/*-----------------------------------------------------------*/
void vStartMathTasks( UBaseType_t uxPriority )
{
xTaskCreate( vCompetingMathTask1, "Math1", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask2, "Math2", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask3, "Math3", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask4, "Math4", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL );
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask1, pvParameters )
{
volatile portDOUBLE d1, d2, d3, d4;
volatile uint16_t *pusTaskCheckVariable;
volatile portDOUBLE dAnswer;
short sError = pdFALSE;
/* Some ports require that tasks that use a hardware floating point unit
tell the kernel that they require a floating point context before any
floating point instructions are executed. */
portTASK_USES_FLOATING_POINT();
d1 = 123.4567;
d2 = 2345.6789;
d3 = -918.222;
dAnswer = ( d1 + d2 ) * d3;
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( volatile uint16_t * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for(;;)
{
d1 = 123.4567;
d2 = 2345.6789;
d3 = -918.222;
d4 = ( d1 + d2 ) * d3;
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( fabs( d4 - dAnswer ) > 0.001 )
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct then set set the check
variable. The check variable will get set to pdFALSE each time
xAreMathsTaskStillRunning() is executed. */
( *pusTaskCheckVariable ) = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask2, pvParameters )
{
volatile portDOUBLE d1, d2, d3, d4;
volatile uint16_t *pusTaskCheckVariable;
volatile portDOUBLE dAnswer;
short sError = pdFALSE;
/* Some ports require that tasks that use a hardware floating point unit
tell the kernel that they require a floating point context before any
floating point instructions are executed. */
portTASK_USES_FLOATING_POINT();
d1 = -389.38;
d2 = 32498.2;
d3 = -2.0001;
dAnswer = ( d1 / d2 ) * d3;
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( volatile uint16_t * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for( ;; )
{
d1 = -389.38;
d2 = 32498.2;
d3 = -2.0001;
d4 = ( d1 / d2 ) * d3;
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( fabs( d4 - dAnswer ) > 0.001 )
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct then set set the check
variable. The check variable will get set to pdFALSE each time
xAreMathsTaskStillRunning() is executed. */
( *pusTaskCheckVariable ) = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask3, pvParameters )
{
volatile portDOUBLE *pdArray, dTotal1, dTotal2, dDifference;
volatile uint16_t *pusTaskCheckVariable;
const size_t xArraySize = 10;
size_t xPosition;
short sError = pdFALSE;
/* Some ports require that tasks that use a hardware floating point unit
tell the kernel that they require a floating point context before any
floating point instructions are executed. */
portTASK_USES_FLOATING_POINT();
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( volatile uint16_t * ) pvParameters;
pdArray = ( portDOUBLE * ) pvPortMalloc( xArraySize * sizeof( portDOUBLE ) );
/* Keep filling an array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
dTotal1 = 0.0;
dTotal2 = 0.0;
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
pdArray[ xPosition ] = ( portDOUBLE ) xPosition + 5.5;
dTotal1 += ( portDOUBLE ) xPosition + 5.5;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
dTotal2 += pdArray[ xPosition ];
}
dDifference = dTotal1 - dTotal2;
if( fabs( dDifference ) > 0.001 )
{
sError = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
if( sError == pdFALSE )
{
/* If the calculation has always been correct then set set the check
variable. The check variable will get set to pdFALSE each time
xAreMathsTaskStillRunning() is executed. */
( *pusTaskCheckVariable ) = pdTRUE;
}
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask4, pvParameters )
{
volatile portDOUBLE *pdArray, dTotal1, dTotal2, dDifference;
volatile uint16_t *pusTaskCheckVariable;
const size_t xArraySize = 10;
size_t xPosition;
short sError = pdFALSE;
/* Some ports require that tasks that use a hardware floating point unit
tell the kernel that they require a floating point context before any
floating point instructions are executed. */
portTASK_USES_FLOATING_POINT();
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( volatile uint16_t * ) pvParameters;
pdArray = ( portDOUBLE * ) pvPortMalloc( xArraySize * sizeof( portDOUBLE ) );
/* Keep filling an array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
dTotal1 = 0.0;
dTotal2 = 0.0;
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
pdArray[ xPosition ] = ( portDOUBLE ) xPosition * 12.123;
dTotal1 += ( portDOUBLE ) xPosition * 12.123;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
dTotal2 += pdArray[ xPosition ];
}
dDifference = dTotal1 - dTotal2;
if( fabs( dDifference ) > 0.001 )
{
sError = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
if( sError == pdFALSE )
{
/* If the calculation has always been correct then set set the check
variable. The check variable will get set to pdFALSE each time
xAreMathsTaskStillRunning() is executed. */
( *pusTaskCheckVariable ) = pdTRUE;
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreMathsTaskStillRunning( void )
{
BaseType_t xReturn = pdPASS, xTask;
/* Check the maths tasks are still running by ensuring their check variables
have been set to pdPASS. */
for( xTask = 0; xTask < mathNUMBER_OF_TASKS; xTask++ )
{
if( usTaskCheck[ xTask ] != pdTRUE )
{
/* The check has not been set so the associated task has either
stalled or detected an error. */
xReturn = pdFAIL;
}
else
{
/* Reset the variable so it can be checked again the next time this
function is executed. */
usTaskCheck[ xTask ] = pdFALSE;
}
}
return xReturn;
}

View File

@ -0,0 +1,163 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Creates one or more tasks that repeatedly perform a set of integer
* calculations. The result of each run-time calculation is compared to the
* known expected result - with a mismatch being indicative of an error in the
* context switch mechanism.
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "integer.h"
/* The constants used in the calculation. */
#define intgCONST1 ( ( long ) 123 )
#define intgCONST2 ( ( long ) 234567 )
#define intgCONST3 ( ( long ) -3 )
#define intgCONST4 ( ( long ) 7 )
#define intgEXPECTED_ANSWER ( ( ( intgCONST1 + intgCONST2 ) * intgCONST3 ) / intgCONST4 )
#define intgSTACK_SIZE configMINIMAL_STACK_SIZE
/* As this is the minimal version, we will only create one task. */
#define intgNUMBER_OF_TASKS ( 1 )
/* The task function. Repeatedly performs a 32 bit calculation, checking the
result against the expected result. If the result is incorrect then the
context switch must have caused some corruption. */
static portTASK_FUNCTION_PROTO( vCompeteingIntMathTask, pvParameters );
/* Variables that are set to true within the calculation task to indicate
that the task is still executing. The check task sets the variable back to
false, flagging an error if the variable is still false the next time it
is called. */
static BaseType_t xTaskCheck[ intgNUMBER_OF_TASKS ] = { ( BaseType_t ) pdFALSE };
/*-----------------------------------------------------------*/
void vStartIntegerMathTasks( UBaseType_t uxPriority )
{
short sTask;
for( sTask = 0; sTask < intgNUMBER_OF_TASKS; sTask++ )
{
xTaskCreate( vCompeteingIntMathTask, "IntMath", intgSTACK_SIZE, ( void * ) &( xTaskCheck[ sTask ] ), uxPriority, ( TaskHandle_t * ) NULL );
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompeteingIntMathTask, pvParameters )
{
/* These variables are all effectively set to constants so they are volatile to
ensure the compiler does not just get rid of them. */
volatile long lValue;
short sError = pdFALSE;
volatile BaseType_t *pxTaskHasExecuted;
/* Set a pointer to the variable we are going to set to true each
iteration. This is also a good test of the parameter passing mechanism
within each port. */
pxTaskHasExecuted = ( volatile BaseType_t * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for( ;; )
{
/* Perform the calculation. This will store partial value in
registers, resulting in a good test of the context switch mechanism. */
lValue = intgCONST1;
lValue += intgCONST2;
/* Yield in case cooperative scheduling is being used. */
#if configUSE_PREEMPTION == 0
{
taskYIELD();
}
#endif
/* Finish off the calculation. */
lValue *= intgCONST3;
lValue /= intgCONST4;
/* If the calculation is found to be incorrect we stop setting the
TaskHasExecuted variable so the check task can see an error has
occurred. */
if( lValue != intgEXPECTED_ANSWER ) /*lint !e774 volatile used to prevent this being optimised out. */
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* We have not encountered any errors, so set the flag that show
we are still executing. This will be periodically cleared by
the check task. */
portENTER_CRITICAL();
*pxTaskHasExecuted = pdTRUE;
portEXIT_CRITICAL();
}
/* Yield in case cooperative scheduling is being used. */
#if configUSE_PREEMPTION == 0
{
taskYIELD();
}
#endif
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreIntegerMathsTaskStillRunning( void )
{
BaseType_t xReturn = pdTRUE;
short sTask;
/* Check the maths tasks are still running by ensuring their check variables
are still being set to true. */
for( sTask = 0; sTask < intgNUMBER_OF_TASKS; sTask++ )
{
if( xTaskCheck[ sTask ] == pdFALSE )
{
/* The check has not incremented so an error exists. */
xReturn = pdFALSE;
}
/* Reset the check variable so we can tell if it has been set by
the next time around. */
xTaskCheck[ sTask ] = pdFALSE;
}
return xReturn;
}

View File

@ -0,0 +1,2 @@
This directory contains the implementation of the "common demo tasks". These
are test tasks and demo tasks that are used by nearly all the demo applications.

View File

@ -0,0 +1,416 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
The tasks defined on this page demonstrate the use of recursive mutexes.
For recursive mutex functionality the created mutex should be created using
xSemaphoreCreateRecursiveMutex(), then be manipulated
using the xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() API
functions.
This demo creates three tasks all of which access the same recursive mutex:
prvRecursiveMutexControllingTask() has the highest priority so executes
first and grabs the mutex. It then performs some recursive accesses -
between each of which it sleeps for a short period to let the lower
priority tasks execute. When it has completed its demo functionality
it gives the mutex back before suspending itself.
prvRecursiveMutexBlockingTask() attempts to access the mutex by performing
a blocking 'take'. The blocking task has a lower priority than the
controlling task so by the time it executes the mutex has already been
taken by the controlling task, causing the blocking task to block. It
does not unblock until the controlling task has given the mutex back,
and it does not actually run until the controlling task has suspended
itself (due to the relative priorities). When it eventually does obtain
the mutex all it does is give the mutex back prior to also suspending
itself. At this point both the controlling task and the blocking task are
suspended.
prvRecursiveMutexPollingTask() runs at the idle priority. It spins round
a tight loop attempting to obtain the mutex with a non-blocking call. As
the lowest priority task it will not successfully obtain the mutex until
both the controlling and blocking tasks are suspended. Once it eventually
does obtain the mutex it first unsuspends both the controlling task and
blocking task prior to giving the mutex back - resulting in the polling
task temporarily inheriting the controlling tasks priority.
*/
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo app include files. */
#include "recmutex.h"
/* Priorities assigned to the three tasks. recmuCONTROLLING_TASK_PRIORITY can
be overridden by a definition in FreeRTOSConfig.h. */
#ifndef recmuCONTROLLING_TASK_PRIORITY
#define recmuCONTROLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 )
#endif
#define recmuBLOCKING_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 )
#define recmuPOLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 0 )
/* The recursive call depth. */
#define recmuMAX_COUNT ( 10 )
/* Misc. */
#define recmuSHORT_DELAY ( pdMS_TO_TICKS( 20 ) )
#define recmuNO_DELAY ( ( TickType_t ) 0 )
#define recmu15ms_DELAY ( pdMS_TO_TICKS( 15 ) )
#ifndef recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE
#define recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
#endif
/* The three tasks as described at the top of this file. */
static void prvRecursiveMutexControllingTask( void *pvParameters );
static void prvRecursiveMutexBlockingTask( void *pvParameters );
static void prvRecursiveMutexPollingTask( void *pvParameters );
/* The mutex used by the demo. */
static SemaphoreHandle_t xMutex;
/* Variables used to detect and latch errors. */
static volatile BaseType_t xErrorOccurred = pdFALSE, xControllingIsSuspended = pdFALSE, xBlockingIsSuspended = pdFALSE;
static volatile UBaseType_t uxControllingCycles = 0, uxBlockingCycles = 0, uxPollingCycles = 0;
/* Handles of the two higher priority tasks, required so they can be resumed
(unsuspended). */
static TaskHandle_t xControllingTaskHandle, xBlockingTaskHandle;
/*-----------------------------------------------------------*/
void vStartRecursiveMutexTasks( void )
{
/* Just creates the mutex and the three tasks. */
xMutex = xSemaphoreCreateRecursiveMutex();
if( xMutex != NULL )
{
/* vQueueAddToRegistry() adds the mutex to the registry, if one is
in use. The registry is provided as a means for kernel aware
debuggers to locate mutex and has no purpose if a kernel aware debugger
is not being used. The call to vQueueAddToRegistry() will be removed
by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
defined to be less than 1. */
vQueueAddToRegistry( ( QueueHandle_t ) xMutex, "Recursive_Mutex" );
xTaskCreate( prvRecursiveMutexControllingTask, "Rec1", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuCONTROLLING_TASK_PRIORITY, &xControllingTaskHandle );
xTaskCreate( prvRecursiveMutexBlockingTask, "Rec2", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuBLOCKING_TASK_PRIORITY, &xBlockingTaskHandle );
xTaskCreate( prvRecursiveMutexPollingTask, "Rec3", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuPOLLING_TASK_PRIORITY, NULL );
}
}
/*-----------------------------------------------------------*/
static void prvRecursiveMutexControllingTask( void *pvParameters )
{
UBaseType_t ux;
/* Just to remove compiler warning. */
( void ) pvParameters;
for( ;; )
{
/* Should not be able to 'give' the mutex, as we have not yet 'taken'
it. The first time through, the mutex will not have been used yet,
subsequent times through, at this point the mutex will be held by the
polling task. */
if( xSemaphoreGiveRecursive( xMutex ) == pdPASS )
{
xErrorOccurred = pdTRUE;
}
for( ux = 0; ux < recmuMAX_COUNT; ux++ )
{
/* We should now be able to take the mutex as many times as
we like.
The first time through the mutex will be immediately available, on
subsequent times through the mutex will be held by the polling task
at this point and this Take will cause the polling task to inherit
the priority of this task. In this case the block time must be
long enough to ensure the polling task will execute again before the
block time expires. If the block time does expire then the error
flag will be set here. */
if( xSemaphoreTakeRecursive( xMutex, recmu15ms_DELAY ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
/* Ensure the other task attempting to access the mutex (and the
other demo tasks) are able to execute to ensure they either block
(where a block time is specified) or return an error (where no
block time is specified) as the mutex is held by this task. */
vTaskDelay( recmuSHORT_DELAY );
}
/* For each time we took the mutex, give it back. */
for( ux = 0; ux < recmuMAX_COUNT; ux++ )
{
/* Ensure the other task attempting to access the mutex (and the
other demo tasks) are able to execute. */
vTaskDelay( recmuSHORT_DELAY );
/* We should now be able to give the mutex as many times as we
took it. When the mutex is available again the Blocking task
should be unblocked but not run because it has a lower priority
than this task. The polling task should also not run at this point
as it too has a lower priority than this task. */
if( xSemaphoreGiveRecursive( xMutex ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
}
/* Having given it back the same number of times as it was taken, we
should no longer be the mutex owner, so the next give should fail. */
if( xSemaphoreGiveRecursive( xMutex ) == pdPASS )
{
xErrorOccurred = pdTRUE;
}
/* Keep count of the number of cycles this task has performed so a
stall can be detected. */
uxControllingCycles++;
/* Suspend ourselves so the blocking task can execute. */
xControllingIsSuspended = pdTRUE;
vTaskSuspend( NULL );
xControllingIsSuspended = pdFALSE;
}
}
/*-----------------------------------------------------------*/
static void prvRecursiveMutexBlockingTask( void *pvParameters )
{
/* Just to remove compiler warning. */
( void ) pvParameters;
for( ;; )
{
/* This task will run while the controlling task is blocked, and the
controlling task will block only once it has the mutex - therefore
this call should block until the controlling task has given up the
mutex, and not actually execute past this call until the controlling
task is suspended. portMAX_DELAY - 1 is used instead of portMAX_DELAY
to ensure the task's state is reported as Blocked and not Suspended in
a later call to configASSERT() (within the polling task). */
if( xSemaphoreTakeRecursive( xMutex, ( portMAX_DELAY - 1 ) ) == pdPASS )
{
if( xControllingIsSuspended != pdTRUE )
{
/* Did not expect to execute until the controlling task was
suspended. */
xErrorOccurred = pdTRUE;
}
else
{
/* Give the mutex back before suspending ourselves to allow
the polling task to obtain the mutex. */
if( xSemaphoreGiveRecursive( xMutex ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
xBlockingIsSuspended = pdTRUE;
vTaskSuspend( NULL );
xBlockingIsSuspended = pdFALSE;
}
}
else
{
/* We should not leave the xSemaphoreTakeRecursive() function
until the mutex was obtained. */
xErrorOccurred = pdTRUE;
}
/* The controlling and blocking tasks should be in lock step. */
if( uxControllingCycles != ( uxBlockingCycles + 1 ) )
{
xErrorOccurred = pdTRUE;
}
/* Keep count of the number of cycles this task has performed so a
stall can be detected. */
uxBlockingCycles++;
}
}
/*-----------------------------------------------------------*/
static void prvRecursiveMutexPollingTask( void *pvParameters )
{
/* Just to remove compiler warning. */
( void ) pvParameters;
for( ;; )
{
/* Keep attempting to obtain the mutex. It should only be obtained when
the blocking task has suspended itself, which in turn should only
happen when the controlling task is also suspended. */
if( xSemaphoreTakeRecursive( xMutex, recmuNO_DELAY ) == pdPASS )
{
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xControllingTaskHandle ) == eSuspended );
configASSERT( eTaskGetState( xBlockingTaskHandle ) == eSuspended );
}
#endif /* INCLUDE_eTaskGetState */
/* Is the blocking task suspended? */
if( ( xBlockingIsSuspended != pdTRUE ) || ( xControllingIsSuspended != pdTRUE ) )
{
xErrorOccurred = pdTRUE;
}
else
{
/* Keep count of the number of cycles this task has performed
so a stall can be detected. */
uxPollingCycles++;
/* We can resume the other tasks here even though they have a
higher priority than the polling task. When they execute they
will attempt to obtain the mutex but fail because the polling
task is still the mutex holder. The polling task (this task)
will then inherit the higher priority. The Blocking task will
block indefinitely when it attempts to obtain the mutex, the
Controlling task will only block for a fixed period and an
error will be latched if the polling task has not returned the
mutex by the time this fixed period has expired. */
vTaskResume( xBlockingTaskHandle );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
vTaskResume( xControllingTaskHandle );
#if( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
/* The other two tasks should now have executed and no longer
be suspended. */
if( ( xBlockingIsSuspended == pdTRUE ) || ( xControllingIsSuspended == pdTRUE ) )
{
xErrorOccurred = pdTRUE;
}
#if( INCLUDE_uxTaskPriorityGet == 1 )
{
/* Check priority inherited. */
configASSERT( uxTaskPriorityGet( NULL ) == recmuCONTROLLING_TASK_PRIORITY );
}
#endif /* INCLUDE_uxTaskPriorityGet */
#if( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xControllingTaskHandle ) == eBlocked );
configASSERT( eTaskGetState( xBlockingTaskHandle ) == eBlocked );
}
#endif /* INCLUDE_eTaskGetState */
/* Release the mutex, disinheriting the higher priority again. */
if( xSemaphoreGiveRecursive( xMutex ) != pdPASS )
{
xErrorOccurred = pdTRUE;
}
#if( INCLUDE_uxTaskPriorityGet == 1 )
{
/* Check priority disinherited. */
configASSERT( uxTaskPriorityGet( NULL ) == recmuPOLLING_TASK_PRIORITY );
}
#endif /* INCLUDE_uxTaskPriorityGet */
}
}
#if configUSE_PREEMPTION == 0
{
taskYIELD();
}
#endif
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreRecursiveMutexTasksStillRunning( void )
{
BaseType_t xReturn;
static UBaseType_t uxLastControllingCycles = 0, uxLastBlockingCycles = 0, uxLastPollingCycles = 0;
/* Is the controlling task still cycling? */
if( uxLastControllingCycles == uxControllingCycles )
{
xErrorOccurred = pdTRUE;
}
else
{
uxLastControllingCycles = uxControllingCycles;
}
/* Is the blocking task still cycling? */
if( uxLastBlockingCycles == uxBlockingCycles )
{
xErrorOccurred = pdTRUE;
}
else
{
uxLastBlockingCycles = uxBlockingCycles;
}
/* Is the polling task still cycling? */
if( uxLastPollingCycles == uxPollingCycles )
{
xErrorOccurred = pdTRUE;
}
else
{
uxLastPollingCycles = uxPollingCycles;
}
if( xErrorOccurred == pdTRUE )
{
xReturn = pdFAIL;
}
else
{
xReturn = pdPASS;
}
return xReturn;
}

View File

@ -0,0 +1,269 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Creates two sets of two tasks. The tasks within a set share a variable, access
* to which is guarded by a semaphore.
*
* Each task starts by attempting to obtain the semaphore. On obtaining a
* semaphore a task checks to ensure that the guarded variable has an expected
* value. It then clears the variable to zero before counting it back up to the
* expected value in increments of 1. After each increment the variable is checked
* to ensure it contains the value to which it was just set. When the starting
* value is again reached the task releases the semaphore giving the other task in
* the set a chance to do exactly the same thing. The starting value is high
* enough to ensure that a tick is likely to occur during the incrementing loop.
*
* An error is flagged if at any time during the process a shared variable is
* found to have a value other than that expected. Such an occurrence would
* suggest an error in the mutual exclusion mechanism by which access to the
* variable is restricted.
*
* The first set of two tasks poll their semaphore. The second set use blocking
* calls.
*
*/
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* Demo app include files. */
#include "semtest.h"
/* The value to which the shared variables are counted. */
#define semtstBLOCKING_EXPECTED_VALUE ( ( uint32_t ) 0xfff )
#define semtstNON_BLOCKING_EXPECTED_VALUE ( ( uint32_t ) 0xff )
#define semtstSTACK_SIZE configMINIMAL_STACK_SIZE
#define semtstNUM_TASKS ( 4 )
#define semtstDELAY_FACTOR ( ( TickType_t ) 10 )
/* The task function as described at the top of the file. */
static portTASK_FUNCTION_PROTO( prvSemaphoreTest, pvParameters );
/* Structure used to pass parameters to each task. */
typedef struct SEMAPHORE_PARAMETERS
{
SemaphoreHandle_t xSemaphore;
volatile uint32_t *pulSharedVariable;
TickType_t xBlockTime;
} xSemaphoreParameters;
/* Variables used to check that all the tasks are still running without errors. */
static volatile short sCheckVariables[ semtstNUM_TASKS ] = { 0 };
static volatile short sNextCheckVariable = 0;
/*-----------------------------------------------------------*/
void vStartSemaphoreTasks( UBaseType_t uxPriority )
{
xSemaphoreParameters *pxFirstSemaphoreParameters, *pxSecondSemaphoreParameters;
const TickType_t xBlockTime = ( TickType_t ) 100;
/* Create the structure used to pass parameters to the first two tasks. */
pxFirstSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );
if( pxFirstSemaphoreParameters != NULL )
{
/* Create the semaphore used by the first two tasks. */
pxFirstSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary();
if( pxFirstSemaphoreParameters->xSemaphore != NULL )
{
xSemaphoreGive( pxFirstSemaphoreParameters->xSemaphore );
/* Create the variable which is to be shared by the first two tasks. */
pxFirstSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) );
/* Initialise the share variable to the value the tasks expect. */
*( pxFirstSemaphoreParameters->pulSharedVariable ) = semtstNON_BLOCKING_EXPECTED_VALUE;
/* The first two tasks do not block on semaphore calls. */
pxFirstSemaphoreParameters->xBlockTime = ( TickType_t ) 0;
/* Spawn the first two tasks. As they poll they operate at the idle priority. */
xTaskCreate( prvSemaphoreTest, "PolSEM1", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );
xTaskCreate( prvSemaphoreTest, "PolSEM2", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );
/* vQueueAddToRegistry() adds the semaphore to the registry, if one
is in use. The registry is provided as a means for kernel aware
debuggers to locate semaphores and has no purpose if a kernel aware
debugger is not being used. The call to vQueueAddToRegistry() will
be removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
defined or is defined to be less than 1. */
vQueueAddToRegistry( ( QueueHandle_t ) pxFirstSemaphoreParameters->xSemaphore, "Counting_Sem_1" );
}
}
/* Do exactly the same to create the second set of tasks, only this time
provide a block time for the semaphore calls. */
pxSecondSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );
if( pxSecondSemaphoreParameters != NULL )
{
pxSecondSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary();
if( pxSecondSemaphoreParameters->xSemaphore != NULL )
{
xSemaphoreGive( pxSecondSemaphoreParameters->xSemaphore );
pxSecondSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) );
*( pxSecondSemaphoreParameters->pulSharedVariable ) = semtstBLOCKING_EXPECTED_VALUE;
pxSecondSemaphoreParameters->xBlockTime = xBlockTime / portTICK_PERIOD_MS;
xTaskCreate( prvSemaphoreTest, "BlkSEM1", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );
xTaskCreate( prvSemaphoreTest, "BlkSEM2", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );
/* vQueueAddToRegistry() adds the semaphore to the registry, if one
is in use. The registry is provided as a means for kernel aware
debuggers to locate semaphores and has no purpose if a kernel aware
debugger is not being used. The call to vQueueAddToRegistry() will
be removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
defined or is defined to be less than 1. */
vQueueAddToRegistry( ( QueueHandle_t ) pxSecondSemaphoreParameters->xSemaphore, "Counting_Sem_2" );
}
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( prvSemaphoreTest, pvParameters )
{
xSemaphoreParameters *pxParameters;
volatile uint32_t *pulSharedVariable, ulExpectedValue;
uint32_t ulCounter;
short sError = pdFALSE, sCheckVariableToUse;
/* See which check variable to use. sNextCheckVariable is not semaphore
protected! */
portENTER_CRITICAL();
sCheckVariableToUse = sNextCheckVariable;
sNextCheckVariable++;
portEXIT_CRITICAL();
/* A structure is passed in as the parameter. This contains the shared
variable being guarded. */
pxParameters = ( xSemaphoreParameters * ) pvParameters;
pulSharedVariable = pxParameters->pulSharedVariable;
/* If we are blocking we use a much higher count to ensure loads of context
switches occur during the count. */
if( pxParameters->xBlockTime > ( TickType_t ) 0 )
{
ulExpectedValue = semtstBLOCKING_EXPECTED_VALUE;
}
else
{
ulExpectedValue = semtstNON_BLOCKING_EXPECTED_VALUE;
}
for( ;; )
{
/* Try to obtain the semaphore. */
if( xSemaphoreTake( pxParameters->xSemaphore, pxParameters->xBlockTime ) == pdPASS )
{
/* We have the semaphore and so expect any other tasks using the
shared variable to have left it in the state we expect to find
it. */
if( *pulSharedVariable != ulExpectedValue )
{
sError = pdTRUE;
}
/* Clear the variable, then count it back up to the expected value
before releasing the semaphore. Would expect a context switch or
two during this time. */
for( ulCounter = ( uint32_t ) 0; ulCounter <= ulExpectedValue; ulCounter++ )
{
*pulSharedVariable = ulCounter;
if( *pulSharedVariable != ulCounter )
{
sError = pdTRUE;
}
}
/* Release the semaphore, and if no errors have occurred increment the check
variable. */
if( xSemaphoreGive( pxParameters->xSemaphore ) == pdFALSE )
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
if( sCheckVariableToUse < semtstNUM_TASKS )
{
( sCheckVariables[ sCheckVariableToUse ] )++;
}
}
/* If we have a block time then we are running at a priority higher
than the idle priority. This task takes a long time to complete
a cycle (deliberately so to test the guarding) so will be starving
out lower priority tasks. Block for some time to allow give lower
priority tasks some processor time. */
vTaskDelay( pxParameters->xBlockTime * semtstDELAY_FACTOR );
}
else
{
if( pxParameters->xBlockTime == ( TickType_t ) 0 )
{
/* We have not got the semaphore yet, so no point using the
processor. We are not blocking when attempting to obtain the
semaphore. */
taskYIELD();
}
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreSemaphoreTasksStillRunning( void )
{
static short sLastCheckVariables[ semtstNUM_TASKS ] = { 0 };
BaseType_t xTask, xReturn = pdTRUE;
for( xTask = 0; xTask < semtstNUM_TASKS; xTask++ )
{
if( sLastCheckVariables[ xTask ] == sCheckVariables[ xTask ] )
{
xReturn = pdFALSE;
}
sLastCheckVariables[ xTask ] = sCheckVariables[ xTask ];
}
return xReturn;
}

View File

@ -0,0 +1,327 @@
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/*
* Creates eight tasks, each of which loops continuously performing a floating
* point calculation - using single precision variables.
*
* All the tasks run at the idle priority and never block or yield. This causes
* all eight tasks to time slice with the idle task. Running at the idle priority
* means that these tasks will get pre-empted any time another task is ready to run
* or a time slice occurs. More often than not the pre-emption will occur mid
* calculation, creating a good test of the schedulers context switch mechanism - a
* calculation producing an unexpected result could be a symptom of a corruption in
* the context of a task.
*/
#include <stdlib.h>
#include <math.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
/* Demo program include files. */
#include "flop.h"
#define mathSTACK_SIZE configMINIMAL_STACK_SIZE
#define mathNUMBER_OF_TASKS ( 8 )
/* Four tasks, each of which performs a different floating point calculation.
Each of the four is created twice. */
static portTASK_FUNCTION_PROTO( vCompetingMathTask1, pvParameters );
static portTASK_FUNCTION_PROTO( vCompetingMathTask2, pvParameters );
static portTASK_FUNCTION_PROTO( vCompetingMathTask3, pvParameters );
static portTASK_FUNCTION_PROTO( vCompetingMathTask4, pvParameters );
/* These variables are used to check that all the tasks are still running. If a
task gets a calculation wrong it will
stop incrementing its check variable. */
static volatile uint16_t usTaskCheck[ mathNUMBER_OF_TASKS ] = { ( uint16_t ) 0 };
/*-----------------------------------------------------------*/
void vStartMathTasks( UBaseType_t uxPriority )
{
xTaskCreate( vCompetingMathTask1, "Math1", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask2, "Math2", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask3, "Math3", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask4, "Math4", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask1, "Math5", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 4 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask2, "Math6", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 5 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask3, "Math7", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 6 ] ), uxPriority, NULL );
xTaskCreate( vCompetingMathTask4, "Math8", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 7 ] ), uxPriority, NULL );
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask1, pvParameters )
{
volatile float f1, f2, f3, f4;
volatile uint16_t *pusTaskCheckVariable;
volatile float fAnswer;
short sError = pdFALSE;
f1 = 123.4567F;
f2 = 2345.6789F;
f3 = -918.222F;
fAnswer = ( f1 + f2 ) * f3;
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( uint16_t * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for(;;)
{
f1 = 123.4567F;
f2 = 2345.6789F;
f3 = -918.222F;
f4 = ( f1 + f2 ) * f3;
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( fabs( f4 - fAnswer ) > 0.001F )
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask2, pvParameters )
{
volatile float f1, f2, f3, f4;
volatile uint16_t *pusTaskCheckVariable;
volatile float fAnswer;
short sError = pdFALSE;
f1 = -389.38F;
f2 = 32498.2F;
f3 = -2.0001F;
fAnswer = ( f1 / f2 ) * f3;
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( uint16_t * ) pvParameters;
/* Keep performing a calculation and checking the result against a constant. */
for( ;; )
{
f1 = -389.38F;
f2 = 32498.2F;
f3 = -2.0001F;
f4 = ( f1 / f2 ) * f3;
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* If the calculation does not match the expected constant, stop the
increment of the check variable. */
if( fabs( f4 - fAnswer ) > 0.001F )
{
sError = pdTRUE;
}
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know
this task is still running okay. */
( *pusTaskCheckVariable )++;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask3, pvParameters )
{
volatile float *pfArray, fTotal1, fTotal2, fDifference, fPosition;
volatile uint16_t *pusTaskCheckVariable;
const size_t xArraySize = 10;
size_t xPosition;
short sError = pdFALSE;
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( uint16_t * ) pvParameters;
pfArray = ( float * ) pvPortMalloc( xArraySize * sizeof( float ) );
/* Keep filling an array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
fTotal1 = 0.0F;
fTotal2 = 0.0F;
fPosition = 0.0F;
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
pfArray[ xPosition ] = fPosition + 5.5F;
fTotal1 += fPosition + 5.5F;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
fTotal2 += pfArray[ xPosition ];
}
fDifference = fTotal1 - fTotal2;
if( fabs( fDifference ) > 0.001F )
{
sError = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vCompetingMathTask4, pvParameters )
{
volatile float *pfArray, fTotal1, fTotal2, fDifference, fPosition;
volatile uint16_t *pusTaskCheckVariable;
const size_t xArraySize = 10;
size_t xPosition;
short sError = pdFALSE;
/* The variable this task increments to show it is still running is passed in
as the parameter. */
pusTaskCheckVariable = ( uint16_t * ) pvParameters;
pfArray = ( float * ) pvPortMalloc( xArraySize * sizeof( float ) );
/* Keep filling an array, keeping a running total of the values placed in the
array. Then run through the array adding up all the values. If the two totals
do not match, stop the check variable from incrementing. */
for( ;; )
{
fTotal1 = 0.0F;
fTotal2 = 0.0F;
fPosition = 0.0F;
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
pfArray[ xPosition ] = fPosition * 12.123F;
fTotal1 += fPosition * 12.123F;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
for( xPosition = 0; xPosition < xArraySize; xPosition++ )
{
fTotal2 += pfArray[ xPosition ];
}
fDifference = fTotal1 - fTotal2;
if( fabs( fDifference ) > 0.001F )
{
sError = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
if( sError == pdFALSE )
{
/* If the calculation has always been correct, increment the check
variable so we know this task is still running okay. */
( *pusTaskCheckVariable )++;
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreMathsTaskStillRunning( void )
{
/* Keep a history of the check variables so we know if they have been incremented
since the last call. */
static uint16_t usLastTaskCheck[ mathNUMBER_OF_TASKS ] = { ( uint16_t ) 0 };
BaseType_t xReturn = pdTRUE, xTask;
/* Check the maths tasks are still running by ensuring their check variables
are still incrementing. */
for( xTask = 0; xTask < mathNUMBER_OF_TASKS; xTask++ )
{
if( usTaskCheck[ xTask ] == usLastTaskCheck[ xTask ] )
{
/* The check has not incremented so an error exists. */
xReturn = pdFALSE;
}
usLastTaskCheck[ xTask ] = usTaskCheck[ xTask ];
}
return xReturn;
}

View File

@ -0,0 +1,14 @@
Contains the files that are not specific to any one demo, but are instead used
by all the demo applications.
Most of the directories are now obsolete, and only maintained for backward
compatibility. The directories in active use are:
+ Minimal - this contains the implementation of what are referred to as the
"Standard Demo Tasks". These are used by all the demo applications. Their only
purpose is to demonstrate the FreeRTOS API and test the FreeRTOS features. The
directory is called 'Minimal' as it contains a minimal implementation of files
contained in the 'Full' directory - but the 'Full' directory is no longer used.
+ include - contains header files for the C source files located in the Minimal
directory.

View File

@ -0,0 +1,48 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x200000;
define symbol __ICFEDIT_region_ROM_end__ = 0x21FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x303FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_startup__ = 0x100;
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_ROM_start__;
export symbol __ICFEDIT_region_ROM_end__;
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_startup__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region STA_region = mem:[from __ICFEDIT_region_ROM_start__ size __ICFEDIT_size_startup__];
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__+__ICFEDIT_size_startup__ to __ICFEDIT_region_ROM_end__];
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readwrite };
initialize by copy { section .vectors };
do not initialize { section .noinit };
place in STA_region { section .cstartup };
place in ROM_region { readonly };
place in VEC_region { section .vectors };
place in RAM_region { readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,46 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_SDRAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_SDRAM_end__ = 0x21FFFFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x303FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_startup__ = 0x100;
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_SDRAM_start__;
export symbol __ICFEDIT_region_SDRAM_end__;
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_startup__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region STA_region = mem:[from __ICFEDIT_region_SDRAM_start__ size __ICFEDIT_size_startup__];
define region SDRAM_region = mem:[from __ICFEDIT_region_SDRAM_start__+__ICFEDIT_size_startup__ to __ICFEDIT_region_SDRAM_end__];
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { section .vectors };
do not initialize { section .noinit };
place in STA_region { section .cstartup };
place in VEC_region { section .vectors };
place in SDRAM_region { readonly, readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,36 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x303FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x800;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
do not initialize { section .noinit };
place in VEC_region { section .vectors };
place in RAM_region { section .cstartup, readonly, readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,48 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x200000;
define symbol __ICFEDIT_region_ROM_end__ = 0x23FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x307FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_startup__ = 0x100;
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_ROM_start__;
export symbol __ICFEDIT_region_ROM_end__;
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_startup__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region STA_region = mem:[from __ICFEDIT_region_ROM_start__ size __ICFEDIT_size_startup__];
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__+__ICFEDIT_size_startup__ to __ICFEDIT_region_ROM_end__];
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readwrite };
initialize by copy { section .vectors };
do not initialize { section .noinit };
place in STA_region { section .cstartup };
place in ROM_region { readonly };
place in VEC_region { section .vectors };
place in RAM_region { readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,46 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_SDRAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_SDRAM_end__ = 0x21FFFFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x307FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_startup__ = 0x100;
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_SDRAM_start__;
export symbol __ICFEDIT_region_SDRAM_end__;
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_startup__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region STA_region = mem:[from __ICFEDIT_region_SDRAM_start__ size __ICFEDIT_size_startup__];
define region SDRAM_region = mem:[from __ICFEDIT_region_SDRAM_start__+__ICFEDIT_size_startup__ to __ICFEDIT_region_SDRAM_end__];
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { section .vectors };
do not initialize { section .noinit };
place in STA_region { section .cstartup };
place in VEC_region { section .vectors };
place in SDRAM_region { readonly, readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,36 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x307FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x800;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
do not initialize { section .noinit };
place in VEC_region { section .vectors };
place in RAM_region { section .cstartup, readonly, readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,48 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x200000;
define symbol __ICFEDIT_region_ROM_end__ = 0x27FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x307FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_startup__ = 0x100;
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_ROM_start__;
export symbol __ICFEDIT_region_ROM_end__;
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_startup__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region STA_region = mem:[from __ICFEDIT_region_ROM_start__ size __ICFEDIT_size_startup__];
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__+__ICFEDIT_size_startup__ to __ICFEDIT_region_ROM_end__];
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readwrite };
initialize by copy { section .vectors };
do not initialize { section .noinit };
place in STA_region { section .cstartup };
place in ROM_region { readonly };
place in VEC_region { section .vectors };
place in RAM_region { readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,46 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_SDRAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_SDRAM_end__ = 0x21FFFFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x307FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_startup__ = 0x100;
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x1000;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_SDRAM_start__;
export symbol __ICFEDIT_region_SDRAM_end__;
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_startup__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region STA_region = mem:[from __ICFEDIT_region_SDRAM_start__ size __ICFEDIT_size_startup__];
define region SDRAM_region = mem:[from __ICFEDIT_region_SDRAM_start__+__ICFEDIT_size_startup__ to __ICFEDIT_region_SDRAM_end__];
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { section .vectors };
do not initialize { section .noinit };
place in STA_region { section .cstartup };
place in VEC_region { section .vectors };
place in SDRAM_region { readonly, readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,36 @@
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */
/*-Memory Regions-*/
define symbol __ICFEDIT_region_RAM_start__ = 0x300000;
define symbol __ICFEDIT_region_RAM_end__ = 0x307FFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_vectors__ = 0x100;
define symbol __ICFEDIT_size_cstack__ = 0x800;
define symbol __ICFEDIT_size_svcstack__ = 0x60;
define symbol __ICFEDIT_size_irqstack__ = 0x60;
define symbol __ICFEDIT_size_heap__ = 0x0;
/*-Exports-*/
export symbol __ICFEDIT_region_RAM_start__;
export symbol __ICFEDIT_region_RAM_end__;
export symbol __ICFEDIT_size_vectors__;
export symbol __ICFEDIT_size_cstack__;
export symbol __ICFEDIT_size_svcstack__;
export symbol __ICFEDIT_size_irqstack__;
export symbol __ICFEDIT_size_heap__;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region VEC_region = mem:[from __ICFEDIT_region_RAM_start__ size __ICFEDIT_size_vectors__];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__+__ICFEDIT_size_vectors__ to __ICFEDIT_region_RAM_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { };
define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
do not initialize { section .noinit };
place in VEC_region { section .vectors };
place in RAM_region { section .cstartup, readonly, readwrite, block IRQ_STACK, block SVC_STACK, block CSTACK, block HEAP };

View File

@ -0,0 +1,428 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \dir
/// !Purpose
///
/// Definition and functions for using AT91SAM9XE-related features, such
/// has PIO pins, memories, etc.
///
/// !Usage
/// -# The code for booting the board is provided by board_cstartup.S and
/// board_lowlevel.c.
/// -# For using board PIOs, board characteristics (clock, etc.) and external
/// components, see board.h.
/// -# For manipulating memories (remapping, SDRAM, etc.), see board_memories.h.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \unit
/// !Purpose
///
/// Definition of AT91SAM9XE-EK characteristics, AT91SAM9XE-dependant PIOs and
/// external components interfacing.
///
/// !Usage
/// -# For operating frequency information, see "SAM9XE-EK - Operating frequencies".
/// -# For using portable PIO definitions, see "SAM9XE-EK - PIO definitions".
/// -# Several USB definitions are included here (see "SAM9XE-EK - USB device").
/// -# For external components definitions, see "SAM79260-EK - External components".
/// -# For memory-related definitions, see "SAM79260-EK - Memories".
//------------------------------------------------------------------------------
#ifndef BOARD_H
#define BOARD_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#if defined(at91sam9xe128)
#include "at91sam9xe128/AT91SAM9XE128.h"
#elif defined(at91sam9xe256)
#include "at91sam9xe256/AT91SAM9XE256.h"
#elif defined(at91sam9xe512)
#include "at91sam9xe512/AT91SAM9XE512.h"
#else
#error Board does not support the specified chip.
#endif
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE-EK - Board Description"
/// This page lists several definition related to the board description.
///
/// !Definitions
/// - BOARD_NAME
/// Name of the board.
#define BOARD_NAME "AT91SAM9XE-EK"
/// Board definition.
#define at91sam9xeek
/// Family definition.
#define at91sam9xe
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE-EK - Operating frequencies"
/// This page lists several definition related to the board operating frequency
/// (when using the initialization done by board_lowlevel.c).
///
/// !Definitions
/// - BOARD_MAINOSC
/// - BOARD_MCK
/// Frequency of the board main oscillator.
#define BOARD_MAINOSC 18432000
/// Master clock frequency (when using board_lowlevel.c).
#define BOARD_MCK ((18432000 * 97 / 9) / 2)
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE-EK - USB device"
/// This page lists constants describing several characteristics (controller
/// type, D+ pull-up type, etc.) of the USB device controller of the chip/board.
///
/// !Constants
/// - BOARD_USB_UDP
/// - BOARD_USB_PULLUP_INTERNAL
/// - BOARD_USB_NUMENDPOINTS
/// - BOARD_USB_ENDPOINTS_MAXPACKETSIZE
/// - BOARD_USB_ENDPOINTS_BANKS
/// - BOARD_USB_BMATTRIBUTES
/// Chip has a UDP controller.
#define BOARD_USB_UDP
/// Indicates the D+ pull-up is internal to the USB controller.
#define BOARD_USB_PULLUP_INTERNAL
/// Number of endpoints in the USB controller.
#define BOARD_USB_NUMENDPOINTS 6
/// Returns the maximum packet size of the given endpoint.
#define BOARD_USB_ENDPOINTS_MAXPACKETSIZE(i) ((i >= 4) ? 512 : 64)
#define BOARD_USB_ENDPOINTS_MAXPACKETSIZE_FS 64
/// Returns the number of FIFO banks for the given endpoint.
#define BOARD_USB_ENDPOINTS_BANKS(i) (((i == 0) || (i == 3)) ? 1 : 2)
/// USB attributes configuration descriptor (bus or self powered, remote wakeup)
#define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_SELFPOWERED_NORWAKEUP
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE-EK - PIO definitions"
/// This pages lists all the pio definitions contained in board.h. The constants
/// are named using the following convention: PIN_* for a constant which defines
/// a single Pin instance (but may include several PIOs sharing the same
/// controller), and PINS_* for a list of Pin instances.
///
/// !DBGU
/// - PINS_DBGU
///
/// !LEDs
/// - PIN_LED_0
/// - PIN_LED_1
/// - PINS_LEDS
/// - LED_POWER
/// - LED_DS1
///
/// !Push buttons
/// - PIN_PUSHBUTTON_1
/// - PIN_PUSHBUTTON_2
/// - PINS_PUSHBUTTONS
/// - PUSHBUTTON_BP1
/// - PUSHBUTTON_BP2
///
/// !USART0
/// - PIN_USART0_RXD
/// - PIN_USART0_TXD
/// - PIN_USART0_SCK
///
/// !SPI0
/// - PIN_SPI0_MISO
/// - PIN_SPI0_MOSI
/// - PIN_SPI0_SPCK
/// - PINS_SPI0
/// - PIN_SPI0_NPCS0
/// - PIN_SPI0_NPCS1
///
/// !SSC
/// - PINS_SSC_TX
///
/// !USB
/// - PIN_USB_VBUS
///
/// !MCI
/// - PINS_MCI
///
/// !TWI0
/// - PINS_TWI0
/// List of all DBGU pin definitions.
#define PINS_DBGU {(1<<14) | (1<<15), AT91C_BASE_PIOB, AT91C_ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT}
/// LED #0 pin definition.
#define PIN_LED_0 {1 << 9, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
/// LED #1 pin definition.
#define PIN_LED_1 {1 << 6, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
/// List of all LED definitions.
#define PINS_LEDS PIN_LED_0, PIN_LED_1
/// Power LED index.
#define LED_POWER 0
/// DS1 LED index.
#define LED_DS1 1
/// Push button #1 pin definition.
#define PIN_PUSHBUTTON_1 {1 << 30, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_PULLUP}
/// Pusb button #2 pin definition.
#define PIN_PUSHBUTTON_2 {1UL << 31, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_PULLUP}
/// List of all pushbutton pin definitions.
#define PINS_PUSHBUTTONS PIN_PUSHBUTTON_1, PIN_PUSHBUTTON_2
/// Push button #1 index.
#define PUSHBUTTON_BP1 0
/// Push button #2 index.
#define PUSHBUTTON_BP2 1
/// USART0 TXD pin definition.
#define PIN_USART0_TXD {1 << 4, AT91C_BASE_PIOB, AT91C_ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT}
/// USART0 RXD pin definition.
#define PIN_USART0_RXD {1 << 5, AT91C_BASE_PIOB, AT91C_ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT}
/// USART0 RTS pin definition.
#define PIN_USART0_RTS {1 << 26, AT91C_BASE_PIOB, AT91C_ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT}
/// USART0 CTS pin definition.
#define PIN_USART0_CTS {1 << 27, AT91C_BASE_PIOB, AT91C_ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT}
/// USART0 SCK pin definition.
#define PIN_USART0_SCK {1UL << 31, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
/// SPI0 MISO pin definition.
#define PIN_SPI0_MISO {1 << 0, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_PULLUP}
/// SPI0 MOSI pin definition.
#define PIN_SPI0_MOSI {1 << 1, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
/// SPI0 SPCK pin definition.
#define PIN_SPI0_SPCK {1 << 2, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
/// List of SPI0 pin definitions (MISO, MOSI & SPCK).
#define PINS_SPI0 PIN_SPI0_MISO, PIN_SPI0_MOSI, PIN_SPI0_SPCK
/// SPI0 chip select 0 pin definition.
#define PIN_SPI0_NPCS0 {AT91C_PA3_SPI0_NPCS0, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
/// SPI0 chip select 1 pin definition.
#define PIN_SPI0_NPCS1 {AT91C_PC11_SPI0_NPCS1, AT91C_BASE_PIOC, AT91C_ID_PIOC, PIO_PERIPH_B, PIO_DEFAULT}
/// SSC transmitter pins definition.
#define PINS_SSC_TX {0x00038000, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
/// USB VBus monitoring pin definition.
#define PIN_USB_VBUS {1 << 5, AT91C_BASE_PIOC, AT91C_ID_PIOC, PIO_INPUT, PIO_DEFAULT}
/// List of MCI pins definitions.
#define PINS_MCI {0x0000003B, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}, \
{1 << 8, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
/// TWI0 pins definition.
#define PINS_TWI0 {0x01800000, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE-EK - External components"
/// This page lists the definitions related to external on-board components
/// located in the board.h file for the AT91SAM9XE-EK.
///
/// !AT45 Dataflash Card (A)
/// - BOARD_AT45_A_SPI_BASE
/// - BOARD_AT45_A_SPI_ID
/// - BOARD_AT45_A_SPI_PINS
/// - BOARD_AT45_A_SPI
/// - BOARD_AT45_A_NPCS
/// - BOARD_AT45_A_NPCS_PIN
///
/// !AT45 Dataflash (B)
/// - BOARD_AT45_B_SPI_BASE
/// - BOARD_AT45_B_SPI_ID
/// - BOARD_AT45_B_SPI_PINS
/// - BOARD_AT45_B_SPI
/// - BOARD_AT45_B_NPCS
/// - BOARD_AT45_B_NPCS_PIN
///
/// !SD Card
/// - BOARD_SD_MCI_BASE
/// - BOARD_SD_MCI_ID
/// - BOARD_SD_PINS
/// - BOARD_SD_SLOT
///
///
/// !EMAC
/// - AT91C_BASE_EMAC
/// - BOARD_EMAC_POWER_ALWAYS_ON
/// - BOARD_EMAC_MODE_RMII
/// - BOARD_EMAC_PINS
/// - BOARD_EMAC_PIN_TEST
/// - BOARD_EMAC_PIN_RPTR
/// - BOARD_EMAC_RST_PINS
/// - BOARD_EMAC_RUN_PINS
/// Base address of SPI peripheral connected to the dataflash.
#define BOARD_AT45_A_SPI_BASE AT91C_BASE_SPI0
/// Identifier of SPI peripheral connected to the dataflash.
#define BOARD_AT45_A_SPI_ID AT91C_ID_SPI0
/// Pins of the SPI peripheral connected to the dataflash.
#define BOARD_AT45_A_SPI_PINS PINS_SPI0
/// Dataflahs SPI number.
#define BOARD_AT45_A_SPI 0
/// Chip select connected to the dataflash.
#define BOARD_AT45_A_NPCS 0
/// Chip select pin connected to the dataflash.
#define BOARD_AT45_A_NPCS_PIN PIN_SPI0_NPCS0
/// Base address of SPI peripheral connected to the dataflash.
#define BOARD_AT45_B_SPI_BASE AT91C_BASE_SPI0
/// Identifier of SPI peripheral connected to the dataflash.
#define BOARD_AT45_B_SPI_ID AT91C_ID_SPI0
/// Pins of the SPI peripheral connected to the dataflash.
#define BOARD_AT45_B_SPI_PINS PINS_SPI0
/// Dataflahs SPI number.
#define BOARD_AT45_B_SPI 0
/// Chip select connected to the dataflash.
#define BOARD_AT45_B_NPCS 1
/// Chip select pin connected to the dataflash.
#define BOARD_AT45_B_NPCS_PIN PIN_SPI0_NPCS1
/// Base address of SPI peripheral connected to the serialflash.
#define BOARD_AT26_A_SPI_BASE AT91C_BASE_SPI0
/// Identifier of SPI peripheral connected to the dataflash.
#define BOARD_AT26_A_SPI_ID AT91C_ID_SPI0
/// Pins of the SPI peripheral connected to the dataflash.
#define BOARD_AT26_A_SPI_PINS PINS_SPI0
/// Dataflahs SPI number.
#define BOARD_AT26_A_SPI 0
/// Chip select connected to the dataflash.
#define BOARD_AT26_A_NPCS 0
/// Chip select pin connected to the dataflash.
#define BOARD_AT26_A_NPCS_PIN PIN_SPI0_NPCS0
/// Base address of the MCI peripheral connected to the SD card.
#define BOARD_SD_MCI_BASE AT91C_BASE_MCI
/// Peripheral identifier of the MCI connected to the SD card.
#define BOARD_SD_MCI_ID AT91C_ID_MCI
/// MCI pins that shall be configured to access the SD card.
#define BOARD_SD_PINS PINS_MCI
/// MCI slot to which the SD card is connected to.
#define BOARD_SD_SLOT MCI_SD_SLOTB
/// Board EMAC base address
#if !defined(AT91C_BASE_EMAC) && defined(AT91C_BASE_EMACB)
#define AT91C_BASE_EMAC AT91C_BASE_EMACB
#endif
/// Board EMAC power control - ALWAYS ON
#define BOARD_EMAC_POWER_ALWAYS_ON
/// Board EMAC work mode - RMII/MII ( 1 / 0 )
#define BOARD_EMAC_MODE_RMII 1
/// The PIN list of PIO for EMAC
#define BOARD_EMAC_PINS { ((1<<19)|(1<<13)|(1<<12)|(1<<16)|(1<<15)|(1<<14)\
|(1<<17)|(1<<18)|(1<<20)|(1<<21)|(1<<7)),\
AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT},\
{ ((1<<11)|(1<<10)|(1<<26)|(1<<25)|(1<<27)|(1<<22)\
|(1<<29)|(1<<28)),\
AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
/// The power up reset latch PIO for PHY
#define BOARD_EMAC_PIN_TEST {(1<<17), AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
//#define BOARD_EMAC_PIN_RMII : connected to 3v3 (RMII)
// We force the address
// (1<<14) PHY address 0, (1<<15) PHY address 1 (PIO A, perih A)
// (1<<25) PHY address 2, (1<<26) PHY address 3 (PIO A, perih B)
#define BOARD_EMAC_PINS_PHYAD { ((1<<14)|(1<<15)),\
AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT},\
{ ((1<<25)|(1<<26)),\
AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
//#define BOARD_EMAC_PIN_10BT : not connected
#define BOARD_EMAC_PIN_RPTR {(1<<27), AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
/// The PIN Configure list for EMAC on power up reset
#define BOARD_EMAC_RST_PINS BOARD_EMAC_PINS_PHYAD,\
BOARD_EMAC_PIN_TEST,\
BOARD_EMAC_PIN_RPTR
/// The runtime pin configure list for EMAC
#define BOARD_EMAC_RUN_PINS BOARD_EMAC_PINS
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE-EK - Memories"
/// This page lists definitions related to external on-board memories.
///
/// !Embedded Flash
/// - BOARD_FLASH_EEFC
///
/// !SDRAM
/// - BOARD_SDRAM_SIZE
/// - PINS_SDRAM
///
/// !Nandflash
/// - PINS_NANDFLASH
/// - BOARD_NF_COMMAND_ADDR
/// - BOARD_NF_ADDRESS_ADDR
/// - BOARD_NF_DATA_ADDR
/// - BOARD_NF_CE_PIN
/// - BOARD_NF_RB_PIN
/// Indicates chip has an Enhanced EFC.
#define BOARD_FLASH_EEFC
/// Address of the IAP function in ROM.
#define BOARD_FLASH_IAP_ADDRESS 0x100008
/// Board SDRAM size
#define BOARD_SDRAM_SIZE 0x02000000
/// List of all SDRAM pins definitions.
#define PINS_SDRAM {0xFFFF0000, AT91C_BASE_PIOC, AT91C_ID_PIOC, PIO_PERIPH_A, PIO_DEFAULT}
/// Nandflash controller peripheral pins definition.
#define PINS_NANDFLASH BOARD_NF_CE_PIN, BOARD_NF_RB_PIN
/// Nandflash chip enable pin definition.
#define BOARD_NF_CE_PIN {1 << 14, AT91C_BASE_PIOC, AT91C_ID_PIOC, PIO_OUTPUT_1, PIO_DEFAULT}
/// Nandflash ready/busy pin definition.
#define BOARD_NF_RB_PIN {1 << 13, AT91C_BASE_PIOC, AT91C_ID_PIOC, PIO_INPUT, PIO_PULLUP}
/// Address for transferring command bytes to the nandflash.
#define BOARD_NF_COMMAND_ADDR 0x40400000
/// Address for transferring address bytes to the nandflash.
#define BOARD_NF_ADDRESS_ADDR 0x40200000
/// Address for transferring data bytes to the nandflash.
#define BOARD_NF_DATA_ADDR 0x40000000
/// Address for transferring command bytes to the norflash.
#define BOARD_NORFLASH_ADDR 0x10000000
//------------------------------------------------------------------------------
#endif //#ifndef BOARD_H

View File

@ -0,0 +1,169 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 "ISR_Support.h"
/*
IAR startup file for AT91SAM9XE microcontrollers.
*/
MODULE ?cstartup
;; Forward declaration of sections.
SECTION IRQ_STACK:DATA:NOROOT(2)
SECTION CSTACK:DATA:NOROOT(3)
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#define __ASSEMBLY__
#include "board.h"
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
#define ARM_MODE_ABT 0x17
#define ARM_MODE_FIQ 0x11
#define ARM_MODE_IRQ 0x12
#define ARM_MODE_SVC 0x13
#define ARM_MODE_SYS 0x1F
#define I_BIT 0x80
#define F_BIT 0x40
//------------------------------------------------------------------------------
// Startup routine
//------------------------------------------------------------------------------
/*
Exception vectors
*/
SECTION .vectors:CODE:NOROOT(2)
PUBLIC resetVector
PUBLIC irqHandler
EXTERN Undefined_Handler
EXTERN vPortYieldProcessor
EXTERN Prefetch_Handler
EXTERN Abort_Handler
EXTERN FIQ_Handler
ARM
__iar_init$$done: ; The interrupt vector is not needed
; until after copy initialization is done
resetVector:
; All default exception handlers (except reset) are
; defined as weak symbol definitions.
; If a handler is defined by the application it will take precedence.
LDR pc, =resetHandler ; Reset
LDR pc, Undefined_Addr ; Undefined instructions
LDR pc, SWI_Addr ; Software interrupt (SWI/SVC)
LDR pc, Prefetch_Addr ; Prefetch abort
LDR pc, Abort_Addr ; Data abort
B . ; RESERVED
LDR pc, =irqHandler ; IRQ
LDR pc, FIQ_Addr ; FIQ
Undefined_Addr: DCD Undefined_Handler
SWI_Addr: DCD vPortYieldProcessor
Prefetch_Addr: DCD Prefetch_Handler
Abort_Addr: DCD Abort_Handler
FIQ_Addr: DCD FIQ_Handler
/*
Handles incoming interrupt requests by branching to the corresponding
handler, as defined in the AIC. Supports interrupt nesting.
*/
irqHandler:
portSAVE_CONTEXT
/* Write in the IVR to support Protect Mode */
LDR lr, =AT91C_BASE_AIC
LDR r0, [r14, #AIC_IVR]
STR lr, [r14, #AIC_IVR]
/* Branch to C portion of the interrupt handler */
MOV lr, pc
BX r0
/* Acknowledge interrupt */
LDR lr, =AT91C_BASE_AIC
STR lr, [r14, #AIC_EOICR]
portRESTORE_CONTEXT
/*
After a reset, execution starts here, the mode is ARM, supervisor
with interrupts disabled.
Initializes the chip and branches to the main() function.
*/
SECTION .cstartup:CODE:NOROOT(2)
PUBLIC resetHandler
EXTERN LowLevelInit
EXTERN ?main
REQUIRE resetVector
ARM
resetHandler:
/* Set pc to actual code location (i.e. not in remap zone) */
LDR pc, =label
/* Perform low-level initialization of the chip using LowLevelInit() */
label:
LDR r0, =LowLevelInit
LDR r4, =SFE(CSTACK)
MOV sp, r4
MOV lr, pc
BX r0
/* Set up the interrupt stack pointer. */
MSR cpsr_c, #ARM_MODE_IRQ | I_BIT | F_BIT ; Change the mode
LDR sp, =SFE(IRQ_STACK)
/* Set up the SVC stack pointer. */
MSR cpsr_c, #ARM_MODE_SVC | F_BIT ; Change the mode
LDR sp, =SFE(CSTACK)
/* Branch to main() */
LDR r0, =?main
MOV lr, pc
BX r0
/* Loop indefinitely when program is finished */
loop4:
B loop4
END

View File

@ -0,0 +1,194 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "board.h"
#include "board_memories.h"
//------------------------------------------------------------------------------
// Local definitions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "SAM9XE - Oscillator & PLL Parameters"
/// This page lists the parameters which are set for the PLL and main
/// oscillator configuration.
///
/// !Parameters
/// - BOARD_OSCOUNT
/// - BOARD_CKGR_PLLA
/// - BOARD_PLLACOUNT
/// - BOARD_MULA
/// - BOARD_DIVA
/// - BOARD_CKGR_PLLB
/// - BOARD_PLLBCOUNT
/// - BOARD_MULB
/// - BOARD_DIVB
/// - BOARD_USBDIV
/// - BOARD_PRESCALER
/// Main oscillator startup time (in number of slow clock ticks).
#define BOARD_OSCOUNT (AT91C_CKGR_OSCOUNT & (64 << 8))
/// PLLA frequency range.
#define BOARD_CKGR_PLLA (AT91C_CKGR_SRCA | AT91C_CKGR_OUTA_2)
/// PLLA startup time (in number of slow clock ticks).
#define BOARD_PLLACOUNT (63 << 8)
/// PLLA MUL value.
#define BOARD_MULA (AT91C_CKGR_MULA & (96 << 16))
/// PLLA DIV value.
#define BOARD_DIVA (AT91C_CKGR_DIVA & 9)
/// PLLB frequency range
#define BOARD_CKGR_PLLB AT91C_CKGR_OUTB_1
/// PLLB startup time (in number of slow clock ticks).
#define BOARD_PLLBCOUNT BOARD_PLLACOUNT
/// PLLB MUL value.
#define BOARD_MULB (124 << 16)
/// PLLB DIV value.
#define BOARD_DIVB 12
/// USB PLL divisor value to obtain a 48MHz clock.
#define BOARD_USBDIV AT91C_CKGR_USBDIV_2
/// Master clock prescaler value.
#define BOARD_PRESCALER AT91C_PMC_MDIV_2
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Default spurious interrupt handler
//------------------------------------------------------------------------------
void DefaultSpuriousHandler(void)
{
while (1);
}
//------------------------------------------------------------------------------
/// Default handler for fast interrupt requests.
//------------------------------------------------------------------------------
void DefaultFiqHandler(void)
{
while (1);
}
//------------------------------------------------------------------------------
/// Default handler for standard interrupt requests.
//------------------------------------------------------------------------------
void DefaultIrqHandler(void)
{
while (1);
}
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Performs the low-level initialization of the chip.
//------------------------------------------------------------------------------
void LowLevelInit(void)
{
unsigned char i;
// Set flash wait states
//----------------------
AT91C_BASE_EFC->EFC_FMR = 6 << 8;
//#if !defined(sdram)
// Initialize main oscillator
//---------------------------
AT91C_BASE_PMC->PMC_MOR = BOARD_OSCOUNT | AT91C_CKGR_MOSCEN;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS));
// Initialize PLLA at 200MHz (198.656)
AT91C_BASE_PMC->PMC_PLLAR = BOARD_CKGR_PLLA
| BOARD_PLLACOUNT
| BOARD_MULA
| BOARD_DIVA;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCKA));
// Initialize PLLB for USB usage
AT91C_BASE_PMC->PMC_PLLBR = BOARD_USBDIV
| BOARD_CKGR_PLLB
| BOARD_PLLBCOUNT
| BOARD_MULB
| BOARD_DIVB;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCKB));
// Wait for the master clock if it was already initialized
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Switch to fast clock
//---------------------
// Switch to main oscillator + prescaler
AT91C_BASE_PMC->PMC_MCKR = BOARD_PRESCALER;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
// Switch to PLL + prescaler
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLLA_CLK;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
//#endif //#if !defined(sdram)
// Initialize AIC
//---------------
AT91C_BASE_AIC->AIC_IDCR = 0xFFFFFFFF;
AT91C_BASE_AIC->AIC_SVR[0] = (unsigned int) DefaultFiqHandler;
for (i = 1; i < 31; i++) {
AT91C_BASE_AIC->AIC_SVR[i] = (unsigned int) DefaultIrqHandler;
}
AT91C_BASE_AIC->AIC_SPU = (unsigned int) DefaultSpuriousHandler;
// Unstack nested interrupts
for (i = 0; i < 8 ; i++) {
AT91C_BASE_AIC->AIC_EOICR = 0;
}
// Watchdog initialization
//------------------------
AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS;
// Remap
//------
BOARD_RemapRam();
// Disable RTT and PIT interrupts (potential problem when program A
// configures RTT, then program B wants to use PIT only, interrupts
// from the RTT will still occur since they both use AT91C_ID_SYS)
AT91C_BASE_RTTC->RTTC_RTMR &= ~(AT91C_RTTC_ALMIEN | AT91C_RTTC_RTTINCIEN);
AT91C_BASE_PITC->PITC_PIMR &= ~AT91C_PITC_PITIEN;
}

View File

@ -0,0 +1,304 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
#include <pio/pio.h>
//------------------------------------------------------------------------------
// Local macros
//------------------------------------------------------------------------------
/// Reads a register value. Useful to add trace information to read accesses.
#define READ(peripheral, register) (peripheral->register)
/// Writes data in a register. Useful to add trace information to write accesses.
#define WRITE(peripheral, register, value) (peripheral->register = value)
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Changes the mapping of the chip so that the remap area mirrors the
/// internal ROM or the EBI CS0 (depending on the BMS input).
//------------------------------------------------------------------------------
void BOARD_RemapRom(void)
{
WRITE(AT91C_BASE_MATRIX, MATRIX_MRCR, 0);
}
//------------------------------------------------------------------------------
/// Changes the mapping of the chip so that the remap area mirrors the
/// internal RAM.
//------------------------------------------------------------------------------
void BOARD_RemapRam(void)
{
WRITE(AT91C_BASE_MATRIX,
MATRIX_MRCR,
(AT91C_MATRIX_RCA926I | AT91C_MATRIX_RCA926D));
}
//------------------------------------------------------------------------------
/// Initialize and configure the external SDRAM.
//------------------------------------------------------------------------------
void BOARD_ConfigureSdram(void)
{
volatile unsigned int i;
static const Pin pinsSdram = PINS_SDRAM;
volatile unsigned int *pSdram = (unsigned int *) AT91C_EBI_SDRAM;
// Enable corresponding PIOs
PIO_Configure(&pinsSdram, 1);
// Enable EBI chip select for the SDRAM
WRITE(AT91C_BASE_MATRIX, MATRIX_EBI, AT91C_MATRIX_CS1A_SDRAMC);
// CFG Control Register
WRITE(AT91C_BASE_SDRAMC, SDRAMC_CR, AT91C_SDRAMC_NC_9
| AT91C_SDRAMC_NR_13
| AT91C_SDRAMC_CAS_2
| AT91C_SDRAMC_NB_4_BANKS
| AT91C_SDRAMC_DBW_32_BITS
| AT91C_SDRAMC_TWR_2
| AT91C_SDRAMC_TRC_7
| AT91C_SDRAMC_TRP_2
| AT91C_SDRAMC_TRCD_2
| AT91C_SDRAMC_TRAS_5
| AT91C_SDRAMC_TXSR_8);
for (i = 0; i < 1000; i++);
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_NOP_CMD); // Perform NOP
pSdram[0] = 0x00000000;
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_PRCGALL_CMD); // Set PRCHG AL
pSdram[0] = 0x00000000; // Perform PRCHG
for (i = 0; i < 10000; i++);
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 1st CBR
pSdram[1] = 0x00000001; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 2 CBR
pSdram[2] = 0x00000002; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 3 CBR
pSdram[3] = 0x00000003; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 4 CBR
pSdram[4] = 0x00000004; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 5 CBR
pSdram[5] = 0x00000005; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 6 CBR
pSdram[6] = 0x00000006; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 7 CBR
pSdram[7] = 0x00000007; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 8 CBR
pSdram[8] = 0x00000008; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_LMR_CMD); // Set LMR operation
pSdram[9] = 0xcafedede; // Perform LMR burst=1, lat=2
WRITE(AT91C_BASE_SDRAMC, SDRAMC_TR, (BOARD_MCK * 7) / 1000000); // Set Refresh Timer
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_NORMAL_CMD); // Set Normal mode
pSdram[0] = 0x00000000; // Perform Normal mode
}
//------------------------------------------------------------------------------
/// Initialize and configure the SDRAM for a 48 MHz MCK (ROM code clock settings).
//------------------------------------------------------------------------------
void BOARD_ConfigureSdram48MHz(void)
{
volatile unsigned int i;
static const Pin pinsSdram = PINS_SDRAM;
volatile unsigned int *pSdram = (unsigned int *) AT91C_EBI_SDRAM;
// Enable corresponding PIOs
PIO_Configure(&pinsSdram, 1);
// Enable EBI chip select for the SDRAM
WRITE(AT91C_BASE_MATRIX, MATRIX_EBI, AT91C_MATRIX_CS1A_SDRAMC);
// CFG Control Register
WRITE(AT91C_BASE_SDRAMC, SDRAMC_CR, AT91C_SDRAMC_NC_9
| AT91C_SDRAMC_NR_13
| AT91C_SDRAMC_CAS_2
| AT91C_SDRAMC_NB_4_BANKS
| AT91C_SDRAMC_DBW_32_BITS
| AT91C_SDRAMC_TWR_1
| AT91C_SDRAMC_TRC_4
| AT91C_SDRAMC_TRP_1
| AT91C_SDRAMC_TRCD_1
| AT91C_SDRAMC_TRAS_2
| AT91C_SDRAMC_TXSR_3);
for (i = 0; i < 1000; i++);
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_NOP_CMD); // Perform NOP
pSdram[0] = 0x00000000;
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_PRCGALL_CMD); // Set PRCHG AL
pSdram[0] = 0x00000000; // Perform PRCHG
for (i = 0; i < 10000; i++);
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 1st CBR
pSdram[1] = 0x00000001; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 2 CBR
pSdram[2] = 0x00000002; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 3 CBR
pSdram[3] = 0x00000003; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 4 CBR
pSdram[4] = 0x00000004; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 5 CBR
pSdram[5] = 0x00000005; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 6 CBR
pSdram[6] = 0x00000006; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 7 CBR
pSdram[7] = 0x00000007; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_RFSH_CMD); // Set 8 CBR
pSdram[8] = 0x00000008; // Perform CBR
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_LMR_CMD); // Set LMR operation
pSdram[9] = 0xcafedede; // Perform LMR burst=1, lat=2
WRITE(AT91C_BASE_SDRAMC, SDRAMC_TR, (48000000 * 7) / 1000000); // Set Refresh Timer
WRITE(AT91C_BASE_SDRAMC, SDRAMC_MR, AT91C_SDRAMC_MODE_NORMAL_CMD); // Set Normal mode
pSdram[0] = 0x00000000; // Perform Normal mode
}
//------------------------------------------------------------------------------
/// Configures the EBI for NandFlash access. Pins must be configured after or
/// before calling this function.
//------------------------------------------------------------------------------
void BOARD_ConfigureNandFlash(unsigned char busWidth)
{
// Configure EBI
AT91C_BASE_MATRIX->MATRIX_EBI |= AT91C_MATRIX_CS3A_SM;
// Configure SMC
AT91C_BASE_SMC->SMC_SETUP3 = 0x00000000;
AT91C_BASE_SMC->SMC_PULSE3 = 0x00030003;
AT91C_BASE_SMC->SMC_CYCLE3 = 0x00050005;
AT91C_BASE_SMC->SMC_CTRL3 = 0x00002003;
if (busWidth == 8) {
AT91C_BASE_SMC->SMC_CTRL3 |= AT91C_SMC_DBW_WIDTH_EIGTH_BITS;
}
else if (busWidth == 16) {
AT91C_BASE_SMC->SMC_CTRL3 |= AT91C_SMC_DBW_WIDTH_SIXTEEN_BITS;
}
}
//------------------------------------------------------------------------------
/// Configures the EBI for NandFlash access at 48MHz. Pins must be configured
/// after or before calling this function.
//------------------------------------------------------------------------------
void BOARD_ConfigureNandFlash48MHz(unsigned char busWidth)
{
// Configure EBI
AT91C_BASE_CCFG->CCFG_EBICSA |= AT91C_EBI_CS3A_SM;
// Configure SMC
AT91C_BASE_SMC->SMC_SETUP3 = 0x00010001;
AT91C_BASE_SMC->SMC_PULSE3 = 0x04030302;
AT91C_BASE_SMC->SMC_CYCLE3 = 0x00070004;
AT91C_BASE_SMC->SMC_CTRL3 = (AT91C_SMC_READMODE
| AT91C_SMC_WRITEMODE
| AT91C_SMC_NWAITM_NWAIT_DISABLE
| ((0x1 << 16) & AT91C_SMC_TDF));
if (busWidth == 8) {
AT91C_BASE_SMC->SMC_CTRL3 |= AT91C_SMC_DBW_WIDTH_EIGTH_BITS;
}
else if (busWidth == 16) {
AT91C_BASE_SMC->SMC_CTRL3 |= AT91C_SMC_DBW_WIDTH_SIXTEEN_BITS;
}
}
//------------------------------------------------------------------------------
/// Configures the EBI for NorFlash access at 48MHz.
/// \Param busWidth Bus width
//------------------------------------------------------------------------------
void BOARD_ConfigureNorFlash48MHz(unsigned char busWidth)
{
// Configure SMC
AT91C_BASE_SMC->SMC_SETUP0 = 0x00000001;
AT91C_BASE_SMC->SMC_PULSE0 = 0x07070703;
AT91C_BASE_SMC->SMC_CYCLE0 = 0x00070007;
AT91C_BASE_SMC->SMC_CTRL0 = (AT91C_SMC_READMODE
| AT91C_SMC_WRITEMODE
| AT91C_SMC_NWAITM_NWAIT_DISABLE
| ((0x1 << 16) & AT91C_SMC_TDF));
if (busWidth == 8) {
AT91C_BASE_SMC->SMC_CTRL0 |= AT91C_SMC_DBW_WIDTH_EIGTH_BITS;
}
else if (busWidth == 16) {
AT91C_BASE_SMC->SMC_CTRL0 |= AT91C_SMC_DBW_WIDTH_SIXTEEN_BITS;
}
else if (busWidth == 32) {
AT91C_BASE_SMC->SMC_CTRL0 |= AT91C_SMC_DBW_WIDTH_THIRTY_TWO_BITS;
}
}
//------------------------------------------------------------------------------
/// Set flash wait states in the EFC for 48MHz
//------------------------------------------------------------------------------
void BOARD_ConfigureFlash48MHz(void)
{
// Set flash wait states
//----------------------
AT91C_BASE_EFC->EFC_FMR = 6 << 8;
}

View File

@ -0,0 +1,54 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 BOARD_MEMORIES_H
#define BOARD_MEMORIES_H
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void BOARD_RemapRom(void);
extern void BOARD_RemapRam(void);
extern void BOARD_ConfigureSdram(void);
extern void BOARD_ConfigureSdram48MHz(void);
extern void BOARD_ConfigureNandFlash(unsigned char busWidth);
extern void BOARD_ConfigureNandFlash48MHz(unsigned char busWidth);
extern void BOARD_ConfigureNorFlash48MHz(unsigned char busWidth);
extern void BOARD_ConfigureFlash48MHz(void);
#endif //#ifndef BOARD_MEMORIES_H

View File

@ -0,0 +1,679 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "ac97c.h"
#include <board.h>
#include <aic/aic.h>
#include <utility/assert.h>
#include <utility/trace.h>
#include <utility/math.h>
//------------------------------------------------------------------------------
// Local constants
//------------------------------------------------------------------------------
/// Maximum size of one PDC buffer (in bytes).
#define MAX_PDC_COUNTER 65535
//------------------------------------------------------------------------------
// Local types
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// AC97 transfer descriptor. Tracks the status and parameters of a transfer
/// on the AC97 bus.
//------------------------------------------------------------------------------
typedef struct _Ac97Transfer {
/// Buffer containing the slots to send.
unsigned char *pBuffer;
/// Total number of samples to send.
volatile unsigned int numSamples;
/// Optional callback function.
Ac97Callback callback;
/// Optional argument to the callback function.
void *pArg;
} Ac97Transfer;
//------------------------------------------------------------------------------
/// AC97 controller driver structure. Monitors the status of transfers on all
/// AC97 channels.
//------------------------------------------------------------------------------
typedef struct _Ac97c {
/// List of transfers occuring on each channel.
Ac97Transfer transfers[5];
} Ac97c;
//------------------------------------------------------------------------------
// Local variables
//------------------------------------------------------------------------------
/// Global AC97 controller instance.
static Ac97c ac97c;
//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Returns the size of one sample (in bytes) on the given channel.
/// \param channel Channel number.
//------------------------------------------------------------------------------
static unsigned char GetSampleSize(unsigned char channel)
{
unsigned int size = 0;
SANITY_CHECK((channel == AC97C_CHANNEL_A)
|| (channel == AC97C_CHANNEL_B)
|| (channel == AC97C_CHANNEL_CODEC));
// Check selected channel
switch (channel) {
case AC97C_CHANNEL_CODEC: return 2;
case AC97C_CHANNEL_A: size = (AT91C_BASE_AC97C->AC97C_CAMR & AT91C_AC97C_SIZE) >> 16; break;
case AC97C_CHANNEL_B: size = (AT91C_BASE_AC97C->AC97C_CBMR & AT91C_AC97C_SIZE) >> 16; break;
}
// Compute size in bytes given SIZE field
if ((size & 2) != 0) {
return 2;
}
else {
return 4;
}
}
//------------------------------------------------------------------------------
/// Interrupt service routine for Codec, is invoked by AC97C_Handler.
//------------------------------------------------------------------------------
static void CodecHandler(void)
{
unsigned int status;
unsigned int data;
Ac97Transfer *pTransfer = &(ac97c.transfers[AC97C_CODEC_TRANSFER]);
// Read CODEC status register
status = AT91C_BASE_AC97C->AC97C_COSR;
status &= AT91C_BASE_AC97C->AC97C_COMR;
// A sample has been transmitted
if (status & AT91C_AC97C_TXRDY) {
pTransfer->numSamples--;
// If there are remaining samples, transmit one
if (pTransfer->numSamples > 0) {
data = *((unsigned int *) pTransfer->pBuffer);
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_TXRDY);
AT91C_BASE_AC97C->AC97C_COTHR = data;
// Check if transfer is read or write
if ((data & AT91C_AC97C_READ) != 0) {
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY;
}
else {
pTransfer->pBuffer += sizeof(unsigned int);
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY;
}
}
// Transfer finished
else {
AT91C_BASE_AC97C->AC97C_IDR = AT91C_AC97C_COEVT;
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_TXRDY);
if (pTransfer->callback) {
pTransfer->callback(pTransfer->pArg, 0, 0);
}
}
}
// A sample has been received
if (status & AT91C_AC97C_RXRDY) {
// Store sample
data = AT91C_BASE_AC97C->AC97C_CORHR;
*((unsigned int *) pTransfer->pBuffer) = data;
pTransfer->pBuffer += sizeof(unsigned int);
pTransfer->numSamples--;
// Transfer finished
if (pTransfer->numSamples > 0) {
data = *((unsigned int *) pTransfer->pBuffer);
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_RXRDY);
AT91C_BASE_AC97C->AC97C_COTHR = data;
// Check if transfer is read or write
if ((data & AT91C_AC97C_READ) != 0) {
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY;
}
else {
pTransfer->pBuffer += sizeof(unsigned int);
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY;
}
}
else {
AT91C_BASE_AC97C->AC97C_IDR = AT91C_AC97C_COEVT;
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_RXRDY);
if (pTransfer->callback) {
pTransfer->callback(pTransfer->pArg, 0, 0);
}
}
}
}
//------------------------------------------------------------------------------
/// Interrupt service routine for channel A, is invoked by AC97C_Handler.
//------------------------------------------------------------------------------
static void ChannelAHandler(void)
{
unsigned int status;
Ac97Transfer *pTransmit = &(ac97c.transfers[AC97C_CHANNEL_A_TRANSMIT]);
Ac97Transfer *pReceive = &(ac97c.transfers[AC97C_CHANNEL_A_RECEIVE]);
// Read channel A status register
status = AT91C_BASE_AC97C->AC97C_CASR;
// A buffer has been transmitted
if ((status & AT91C_AC97C_ENDTX) != 0) {
// Update transfer information
if (pTransmit->numSamples > MAX_PDC_COUNTER) {
pTransmit->numSamples -= MAX_PDC_COUNTER;
}
else {
pTransmit->numSamples = 0;
}
// Transmit new buffers if necessary
if (pTransmit->numSamples > MAX_PDC_COUNTER) {
// Fill next PDC
AT91C_BASE_AC97C->AC97C_TNPR = (unsigned int) pTransmit->pBuffer;
if (pTransmit->numSamples > 2 * MAX_PDC_COUNTER) {
AT91C_BASE_AC97C->AC97C_TNCR = MAX_PDC_COUNTER;
pTransmit->pBuffer += MAX_PDC_COUNTER * GetSampleSize(AC97C_CHANNEL_A);
}
else {
AT91C_BASE_AC97C->AC97C_TNCR = pTransmit->numSamples - MAX_PDC_COUNTER;
}
}
// Only one buffer remaining
else {
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_ENDTX;
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_TXBUFE;
}
}
// Transmit completed
if ((status & AT91C_AC97C_TXBUFE) != 0) {
pTransmit->numSamples = 0;
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS;
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_TXBUFE;
if (pTransmit->callback) {
pTransmit->callback(pTransmit->pArg, 0, 0);
}
}
// A buffer has been received
if (status & AT91C_AC97C_ENDRX) {
if (pReceive->numSamples > MAX_PDC_COUNTER) {
pReceive->numSamples -= MAX_PDC_COUNTER;
}
else {
pReceive->numSamples = 0;
}
// Transfer remaining samples
if (pReceive->numSamples > MAX_PDC_COUNTER) {
AT91C_BASE_AC97C->AC97C_RNPR = (unsigned int) pReceive->pBuffer;
if (pReceive->numSamples > 2 * MAX_PDC_COUNTER) {
AT91C_BASE_AC97C->AC97C_RNCR = MAX_PDC_COUNTER;
pReceive->pBuffer += MAX_PDC_COUNTER * GetSampleSize(AC97C_CHANNEL_A);
}
else {
AT91C_BASE_AC97C->AC97C_RNCR = pReceive->numSamples - MAX_PDC_COUNTER;
}
}
// Only one buffer remaining
else {
AT91C_BASE_AC97C->AC97C_CAMR &= ~(AT91C_AC97C_ENDRX);
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_RXBUFF;
}
}
// Receive complete
if ((status & AT91C_AC97C_RXBUFF) != 0) {
pReceive->numSamples = 0;
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS;
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_RXBUFF;
if (pReceive->callback) {
pReceive->callback(pReceive->pArg, 0, 0);
}
}
}
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// This handler function must be called by the AC97C interrupt service routine.
/// Identifies which event was activated and calls the associated function.
//------------------------------------------------------------------------------
void AC97C_Handler(void)
{
unsigned int status;
// Get the real interrupt source
status = AT91C_BASE_AC97C->AC97C_SR;
status &= AT91C_BASE_AC97C->AC97C_IMR;
// Check if an event on the codec channel is active
if ((status & AT91C_AC97C_COEVT) != 0) {
CodecHandler();
}
// Check if an event on channel A is active
if ((status & AT91C_AC97C_CAEVT) != 0) {
ChannelAHandler();
}
}
//------------------------------------------------------------------------------
/// Starts a read or write transfer on the given channel
/// \param channel particular channel (AC97C_CHANNEL_A or AC97C_CHANNEL_B).
/// \param pBuffer buffer containing the slots to send.
/// \param numSamples total number of samples to send.
/// \param callback optional callback function.
/// \param pArg optional argument to the callback function.
//------------------------------------------------------------------------------
unsigned char AC97C_Transfer(
unsigned char channel,
unsigned char *pBuffer,
unsigned int numSamples,
Ac97Callback callback,
void *pArg)
{
unsigned int size;
unsigned int data;
Ac97Transfer *pTransfer;
SANITY_CHECK(channel <= 5);
SANITY_CHECK(pBuffer);
SANITY_CHECK(numSamples > 0);
// Check that no transfer is pending on the channel
pTransfer = &(ac97c.transfers[channel]);
if (pTransfer->numSamples > 0) {
trace_LOG(trace_WARNING, "-W- AC97C_Transfer: Channel %d is busy\n\r", channel);
return AC97C_ERROR_BUSY;
}
// Fill transfer information
pTransfer->pBuffer = pBuffer;
pTransfer->numSamples = numSamples;
pTransfer->callback = callback;
pTransfer->pArg = pArg;
// Transmit or receive over codec channel
if (channel == AC97C_CODEC_TRANSFER) {
// Send command
data = *((unsigned int *) pTransfer->pBuffer);
AT91C_BASE_AC97C->AC97C_COTHR = data;
// Check if transfer is read or write
if ((data & AT91C_AC97C_READ) != 0) {
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY;
}
else {
pTransfer->pBuffer += sizeof(unsigned int);
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY;
}
// Enable interrupts
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_COEVT;
}
// Transmit over channel A
else if (channel == AC97C_CHANNEL_A_TRANSMIT) {
// Disable PDC
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS;
// Fill PDC buffers
size = min(pTransfer->numSamples, MAX_PDC_COUNTER);
AT91C_BASE_AC97C->AC97C_TPR = (unsigned int) pTransfer->pBuffer;
AT91C_BASE_AC97C->AC97C_TCR = size;
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
size = min(pTransfer->numSamples - size, MAX_PDC_COUNTER);
if (size > 0) {
AT91C_BASE_AC97C->AC97C_TNPR = (unsigned int) pTransfer->pBuffer;
AT91C_BASE_AC97C->AC97C_TNCR = size;
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
}
// Enable interrupts
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_PDCEN | AT91C_AC97C_ENDTX;
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_CAEVT;
// Start transfer
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTEN;
}
// Receive over channel A
else if (channel == AC97C_CHANNEL_A_RECEIVE) {
// Disable PDC
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS;
// Fill PDC buffers
size = min(pTransfer->numSamples, MAX_PDC_COUNTER);
AT91C_BASE_AC97C->AC97C_RPR = (unsigned int) pTransfer->pBuffer;
AT91C_BASE_AC97C->AC97C_RCR = size;
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
size = min(pTransfer->numSamples - size, MAX_PDC_COUNTER);
if (size > 0) {
AT91C_BASE_AC97C->AC97C_RNPR = (unsigned int) pTransfer->pBuffer;
AT91C_BASE_AC97C->AC97C_RNCR = size;
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
}
// Enable interrupts
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_PDCEN | AT91C_AC97C_ENDRX;
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_CAEVT;
// Start transfer
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTEN;
}
return 0;
}
//------------------------------------------------------------------------------
/// Stop read or write transfer on the given channel.
/// \param channel Channel number.
//------------------------------------------------------------------------------
void AC97C_CancelTransfer(unsigned char channel)
{
unsigned int size = 0;
Ac97Transfer *pTransfer;
SANITY_CHECK(channel <= AC97C_CHANNEL_B_TRANSMIT);
// Save remaining size
pTransfer = &(ac97c.transfers[channel]);
size = pTransfer->numSamples;
pTransfer->numSamples = 0;
// Stop PDC
if (channel == AC97C_CHANNEL_A_TRANSMIT) {
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS;
size -= min(size, MAX_PDC_COUNTER) - AT91C_BASE_AC97C->AC97C_TCR;
}
if (channel == AC97C_CHANNEL_A_RECEIVE) {
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS;
size -= min(size, MAX_PDC_COUNTER) - AT91C_BASE_AC97C->AC97C_RCR;
}
// Invoke callback if provided
if (pTransfer->callback) {
pTransfer->callback(pTransfer->pArg, AC97C_ERROR_STOPPED, size);
}
}
//------------------------------------------------------------------------------
/// Initializes the AC97 controller.
//------------------------------------------------------------------------------
void AC97C_Configure(void)
{
unsigned char channel;
// Enable the AC97 controller peripheral clock
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_AC97C);
// Enable the peripheral and variable rate adjustment
AT91C_BASE_AC97C->AC97C_MR = AT91C_AC97C_ENA | AT91C_AC97C_VRA;
// Unassigns all input & output slots
AC97C_AssignInputSlots(0, 0xFFFF);
AC97C_AssignOutputSlots(0, 0xFFFF);
// Install the AC97C interrupt handler
AT91C_BASE_AC97C->AC97C_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_AC97C, 0, AC97C_Handler);
AIC_EnableIT(AT91C_ID_AC97C);
// Disable PDC transfers
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;
// Clear channel transfers
for (channel = 0; channel < AC97C_CHANNEL_B_TRANSMIT; channel++) {
ac97c.transfers[channel].numSamples = 0;
}
}
//------------------------------------------------------------------------------
/// Configures the desired channel with the given value.
/// \param channel Channel number.
/// \param cfg Configuration value.
//------------------------------------------------------------------------------
void AC97C_ConfigureChannel(unsigned char channel, unsigned int cfg)
{
SANITY_CHECK((channel == AC97C_CHANNEL_A) || (channel == AC97C_CHANNEL_B));
if (channel == AC97C_CHANNEL_A) {
AT91C_BASE_AC97C->AC97C_CAMR = cfg;
}
else {
AT91C_BASE_AC97C->AC97C_CBMR = cfg;
}
}
//------------------------------------------------------------------------------
/// Assigns the desired input slots to a particular channel.
/// \param channel Channel number (or 0 to unassign slots).
/// \param slots Bitfield value of slots to assign.
//------------------------------------------------------------------------------
void AC97C_AssignInputSlots(unsigned char channel, unsigned int slots)
{
unsigned int value;
unsigned int i;
SANITY_CHECK(channel <= AC97C_CHANNEL_B);
// Assign all slots
slots >>= 3;
for (i = 3; i < 15; i++) {
// Check if slots is selected
if (slots & 1) {
value = AT91C_BASE_AC97C->AC97C_ICA;
value &= ~(0x07 << ((i - 3) * 3));
value |= channel << ((i - 3) * 3);
AT91C_BASE_AC97C->AC97C_ICA = value;
}
slots >>= 1;
}
}
//------------------------------------------------------------------------------
/// Assigns the desired output slots to a particular channel.
/// \param channel Channel number (or 0 to unassign slots).
/// \param slots Bitfield value of slots to assign.
//------------------------------------------------------------------------------
void AC97C_AssignOutputSlots(unsigned char channel, unsigned int slots)
{
unsigned int value;
unsigned int i;
SANITY_CHECK(channel <= AC97C_CHANNEL_B);
// Assign all slots
slots >>= 3;
for (i = 3; i < 15; i++) {
// Check if slots is selected
if (slots & 1) {
value = AT91C_BASE_AC97C->AC97C_OCA;
value &= ~(0x07 << ((i - 3) * 3));
value |= channel << ((i - 3) * 3);
AT91C_BASE_AC97C->AC97C_OCA = value;
}
slots >>= 1;
}
}
//------------------------------------------------------------------------------
/// Returns 1 if no transfer is currently pending on the given channel;
/// otherwise, returns 0.
/// \param channel Channel number.
//------------------------------------------------------------------------------
unsigned char AC97C_IsFinished(unsigned char channel)
{
SANITY_CHECK(channel <= AC97C_CHANNEL_B_TRANSMIT);
if (ac97c.transfers[channel].numSamples > 0) {
return 0;
}
else {
return 1;
}
}
//------------------------------------------------------------------------------
/// Convenience function for synchronously sending commands to the codec.
/// \param address Register address.
/// \param data Command data.
//------------------------------------------------------------------------------
void AC97C_WriteCodec(unsigned char address, unsigned short data)
{
unsigned int sample;
sample = (address << 16) | data;
AC97C_Transfer(AC97C_CODEC_TRANSFER, (unsigned char *) &sample, 1, 0, 0);
while (!AC97C_IsFinished(AC97C_CODEC_TRANSFER));
}
//------------------------------------------------------------------------------
/// Convenience function for receiving data from the AC97 codec.
/// \param address Register address.
//------------------------------------------------------------------------------
unsigned short AC97C_ReadCodec(unsigned char address)
{
unsigned int sample;
sample = AT91C_AC97C_READ | (address << 16);
AC97C_Transfer(AC97C_CODEC_TRANSFER, (unsigned char *) &sample, 1, 0, 0);
while (!AC97C_IsFinished(AC97C_CODEC_TRANSFER));
return sample;
}
//------------------------------------------------------------------------------
/// Sets the size in bits of one sample on the given channel.
/// \param channel Channel number.
/// \param size Size of one sample in bits (10, 16, 18 or 24).
//------------------------------------------------------------------------------
void AC97C_SetChannelSize(unsigned char channel, unsigned char size)
{
unsigned int bits = 0;
SANITY_CHECK((size == 10) || (size == 16) || (size == 18) || (size == 24));
SANITY_CHECK((channel == AC97C_CHANNEL_A) || (channel == AC97C_CHANNEL_B));
switch (size) {
case 10 : bits = AT91C_AC97C_SIZE_10_BITS; break;
case 16 : bits = AT91C_AC97C_SIZE_16_BITS; break;
case 18 : bits = AT91C_AC97C_SIZE_18_BITS; break;
case 20 : bits = AT91C_AC97C_SIZE_20_BITS; break;
}
if (channel == AC97C_CHANNEL_A) {
AT91C_BASE_AC97C->AC97C_CAMR &= ~(AT91C_AC97C_SIZE);
AT91C_BASE_AC97C->AC97C_CAMR |= bits;
}
else {
AT91C_BASE_AC97C->AC97C_CBMR &= ~(AT91C_AC97C_SIZE);
AT91C_BASE_AC97C->AC97C_CBMR |= bits;
}
}

View File

@ -0,0 +1,100 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 AC97C_H
#define AC97C_H
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
/// The channel is already busy with a transfer.
#define AC97C_ERROR_BUSY 1
/// The transfer has been stopped by the user.
#define AC97C_ERROR_STOPPED 2
/// Codec channel index.
#define AC97C_CHANNEL_CODEC 0
/// Channel A index.
#define AC97C_CHANNEL_A 1
/// Channel B index.
#define AC97C_CHANNEL_B 2
/// Codec transmit/receive transfer index.
#define AC97C_CODEC_TRANSFER 0
/// Channel A receive transfer index.
#define AC97C_CHANNEL_A_RECEIVE 1
/// Channel A transmit transfer index.
#define AC97C_CHANNEL_A_TRANSMIT 2
/// Channel B receive transfer index.
#define AC97C_CHANNEL_B_RECEIVE 3
/// Channel B transmit transfer index.
#define AC97C_CHANNEL_B_TRANSMIT 4
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
/// AC97C transfer callback function.
typedef void (*Ac97Callback)(void *pArg,
unsigned char status,
unsigned int remaining);
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
extern void AC97C_Configure();
extern void AC97C_ConfigureChannel(unsigned char channel, unsigned int cfg);
extern void AC97C_AssignInputSlots(unsigned char channel, unsigned int slots);
extern void AC97C_AssignOutputSlots(unsigned char channel, unsigned int slots);
extern unsigned char AC97C_Transfer(
unsigned char channel,
unsigned char *pBuffer,
unsigned int numSamples,
Ac97Callback callback,
void *pArg);
extern unsigned char AC97C_IsFinished(unsigned char channel);
extern void AC97C_WriteCodec(unsigned char address, unsigned short data);
extern unsigned short AC97C_ReadCodec(unsigned char address);
extern void AC97C_SetChannelSize(unsigned char channel, unsigned char size);
extern void AC97C_CancelTransfer(unsigned char channel);
#endif //#ifndef AC97C_H

View File

@ -0,0 +1,196 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 trace_LEVEL
#define trace_LEVEL 1
#endif
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "aes.h"
#include <board.h>
#include <utility/trace.h>
#include <utility/assert.h>
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Configures the AES peripheral to encrypt/decrypt, start mode (manual, auto,
/// PDC) and operating mode (ECB, CBC, OFB, CFB, CTR).
/// \param cipher Indicates if the peripheral should encrypt or decrypt data.
/// \param smode Start mode.
/// \param opmode Operating mode.
//------------------------------------------------------------------------------
void AES_Configure(
unsigned char cipher,
unsigned int smode,
unsigned int opmode)
{
trace_LOG(trace_DEBUG, "-D- AES_Configure()\n\r");
SANITY_CHECK((cipher & 0xFFFFFFFE) == 0);
SANITY_CHECK((smode & 0xFFFFFCFF) == 0);
SANITY_CHECK((opmode & 0xFFFF8FFF) == 0);
// Reset the peripheral first
AT91C_BASE_AES->AES_CR = AT91C_AES_SWRST;
// Configure mode register
AT91C_BASE_AES->AES_MR = cipher | smode | opmode;
}
//------------------------------------------------------------------------------
/// Sets the key used by the AES algorithm to cipher the plain text or
/// decipher the encrypted text.
/// \param pKey Pointer to a 16-bytes cipher key.
//------------------------------------------------------------------------------
void AES_SetKey(const unsigned int *pKey)
{
trace_LOG(trace_DEBUG, "-D- AES_SetKey()\n\r");
SANITY_CHECK(pKey);
AT91C_BASE_AES->AES_KEYWxR[0] = pKey[0];
AT91C_BASE_AES->AES_KEYWxR[1] = pKey[1];
AT91C_BASE_AES->AES_KEYWxR[2] = pKey[2];
AT91C_BASE_AES->AES_KEYWxR[3] = pKey[3];
}
//------------------------------------------------------------------------------
/// Sets the initialization vector that is used to encrypt the plain text or
/// decrypt the cipher text in chained block modes (CBC, CFB, OFB & CTR).
/// \param pVector Pointer to a 16-bytes initialization vector.
//------------------------------------------------------------------------------
void AES_SetVector(const unsigned int *pVector)
{
trace_LOG(trace_DEBUG, "-D- AES_SetVector()\n\r");
SANITY_CHECK(pVector);
AT91C_BASE_AES->AES_IVxR[0] = pVector[0];
AT91C_BASE_AES->AES_IVxR[1] = pVector[1];
AT91C_BASE_AES->AES_IVxR[2] = pVector[2];
AT91C_BASE_AES->AES_IVxR[3] = pVector[3];
}
//------------------------------------------------------------------------------
/// Sets the input data of the AES algorithm (i.e. plain text in cipher mode,
/// ciphered text in decipher mode). If auto mode is active, the encryption is
/// started automatically after writing the last word.
/// \param pData Pointer to the 16-bytes data to cipher/decipher.
//------------------------------------------------------------------------------
void AES_SetInputData(const unsigned int *pData)
{
trace_LOG(trace_DEBUG, "-D- AES_SetInputData()\n\r");
SANITY_CHECK(pData);
AT91C_BASE_AES->AES_IDATAxR[0] = pData[0];
AT91C_BASE_AES->AES_IDATAxR[1] = pData[1];
AT91C_BASE_AES->AES_IDATAxR[2] = pData[2];
AT91C_BASE_AES->AES_IDATAxR[3] = pData[3];
}
//------------------------------------------------------------------------------
/// Stores the result of the last AES operation (encrypt/decrypt) in the
/// provided buffer.
/// \param pData Pointer to a 16-bytes buffer.
//------------------------------------------------------------------------------
void AES_GetOutputData(unsigned int *pData)
{
trace_LOG(trace_DEBUG, "-D- AES_GetOutputData()\n\r");
SANITY_CHECK(pData);
pData[0] = AT91C_BASE_AES->AES_ODATAxR[0];
pData[1] = AT91C_BASE_AES->AES_ODATAxR[1];
pData[2] = AT91C_BASE_AES->AES_ODATAxR[2];
pData[3] = AT91C_BASE_AES->AES_ODATAxR[3];
}
//------------------------------------------------------------------------------
/// Sets the input buffer to use when in PDC mode.
/// \param pInput Pointer to the input buffer.
//------------------------------------------------------------------------------
void AES_SetInputBuffer(const unsigned int *pInput)
{
trace_LOG(trace_DEBUG, "-D- AES_SetInputBuffer()\n\r");
SANITY_CHECK(pInput);
AT91C_BASE_AES->AES_TPR = (unsigned int) pInput;
AT91C_BASE_AES->AES_TCR = 4;
}
//------------------------------------------------------------------------------
/// Sets the output buffer to use when in PDC mode.
/// \param pOutput Pointer to the output buffer.
//------------------------------------------------------------------------------
void AES_SetOutputBuffer(unsigned int *pOutput)
{
trace_LOG(trace_DEBUG, "-D- AES_SetOutputBuffer()\n\r");
SANITY_CHECK(pOutput);
AT91C_BASE_AES->AES_RPR = (unsigned int) pOutput;
AT91C_BASE_AES->AES_RCR = 4;
}
//------------------------------------------------------------------------------
/// Starts the encryption/decryption process when in manual or PDC mode. In
/// manual mode, the key and input data must have been entered using
/// AES_SetKey() and AES_SetInputData(). In PDC mode, the key, input & output
/// buffer must have been set using AES_SetKey(), AES_SetInputBuffer() and
/// AES_SetOutputBuffer().
//------------------------------------------------------------------------------
void AES_Start(void)
{
trace_LOG(trace_DEBUG, "AES_Start()\n\r");
SANITY_CHECK(((AT91C_BASE_AES->AES_MR & AT91C_AES_SMOD) == AT91C_AES_SMOD_MANUAL)
|| ((AT91C_BASE_AES->AES_MR & AT91C_AES_SMOD) == AT91C_AES_SMOD_PDC));
// Manual mode
if ((AT91C_BASE_AES->AES_MR & AT91C_AES_SMOD) == AT91C_AES_SMOD_MANUAL) {
AT91C_BASE_AES->AES_CR = AT91C_AES_START;
}
// PDC
else {
AT91C_BASE_AES->AES_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
}
}
//------------------------------------------------------------------------------
/// Returns the current value of the AES interrupt status register.
//------------------------------------------------------------------------------
unsigned int AES_GetStatus(void)
{
trace_LOG(trace_DEBUG, "-D- AES_GetStatus()\n\r");
return AT91C_BASE_AES->AES_ISR;
}

View File

@ -0,0 +1,59 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 AES_H
#define AES_H
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void AES_Configure(
unsigned char cipher,
unsigned int smode,
unsigned int opmode);
extern void AES_SetKey(const unsigned int *pKey);
extern void AES_SetVector(const unsigned int *pVector);
extern void AES_SetInputData(const unsigned int *pData);
extern void AES_GetOutputData(unsigned int *pData);
extern void AES_SetInputBuffer(const unsigned int *pInput);
extern void AES_SetOutputBuffer(unsigned int *pOutput);
extern void AES_Start(void);
extern unsigned int AES_GetStatus(void);
#endif //#ifndef AES_H

View File

@ -0,0 +1,80 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "aic.h"
#include <board.h>
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Configures the interrupt associated with the given source, using the
/// specified mode and interrupt handler.
/// \param source Interrupt source to configure.
/// \param mode Triggering mode of the interrupt.
/// \param handler Interrupt handler function.
//------------------------------------------------------------------------------
void AIC_ConfigureIT(unsigned int source,
unsigned int mode,
void (*handler)( void ))
{
// Disable the interrupt first
AT91C_BASE_AIC->AIC_IDCR = 1 << source;
// Configure mode and handler
AT91C_BASE_AIC->AIC_SMR[source] = mode;
AT91C_BASE_AIC->AIC_SVR[source] = (unsigned int) handler;
// Clear interrupt
AT91C_BASE_AIC->AIC_ICCR = 1 << source;
}
//------------------------------------------------------------------------------
/// Enables interrupts coming from the given (unique) source.
/// \param source Interrupt source to enable.
//------------------------------------------------------------------------------
void AIC_EnableIT(unsigned int source)
{
AT91C_BASE_AIC->AIC_IECR = 1 << source;
}
//------------------------------------------------------------------------------
/// Disables interrupts coming from the given (unique) source.
/// \param source Interrupt source to enable.
//------------------------------------------------------------------------------
void AIC_DisableIT(unsigned int source)
{
AT91C_BASE_AIC->AIC_IDCR = 1 << source;
}

View File

@ -0,0 +1,74 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \dir
/// !Purpose
///
/// Methods and definitions for configuring interrupts using the Advanced
/// Interrupt Controller (AIC).
///
/// !Usage
/// -# Configure an interrupt source using AIC_ConfigureIT
/// -# Enable or disable interrupt generation of a particular source with
/// AIC_EnableIT and AIC_DisableIT.
//------------------------------------------------------------------------------
#ifndef AIC_H
#define AIC_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
#ifndef AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL
/// Redefinition of missing constant.
#define AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE
#endif
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void AIC_ConfigureIT(unsigned int source,
unsigned int mode,
void (*handler)( void ));
extern void AIC_EnableIT(unsigned int source);
extern void AIC_DisableIT(unsigned int source);
#endif //#ifndef AIC_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 _CAN_H
#define _CAN_H
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
#define AT91C_CAN_TIMEOUT 100000
#define AT91C_TEST_NOK 0
#define AT91C_TEST_OK 1
#define CAN_STATUS_SUCCESS 0
#define CAN_STATUS_LOCKED 1
#define CAN_STATUS_ABORTED 2
#define CAN_STATUS_RESET 3
#if defined (AT91C_BASE_CAN)
#define AT91C_BASE_CAN0 AT91C_BASE_CAN
#endif
#if defined (AT91C_ID_CAN)
#define AT91C_ID_CAN0 AT91C_ID_CAN
#endif
#if defined (AT91C_BASE_CAN_MB0)
#define AT91C_BASE_CAN0_MB0 AT91C_BASE_CAN_MB0
#define AT91C_BASE_CAN0_MB1 AT91C_BASE_CAN_MB1
#define AT91C_BASE_CAN0_MB2 AT91C_BASE_CAN_MB2
#define AT91C_BASE_CAN0_MB3 AT91C_BASE_CAN_MB3
#define AT91C_BASE_CAN0_MB4 AT91C_BASE_CAN_MB4
#define AT91C_BASE_CAN0_MB5 AT91C_BASE_CAN_MB5
#define AT91C_BASE_CAN0_MB6 AT91C_BASE_CAN_MB6
#define AT91C_BASE_CAN0_MB7 AT91C_BASE_CAN_MB7
#endif
#if defined (AT91C_BASE_CAN_MB8)
#define AT91C_BASE_CAN0_MB8 AT91C_BASE_CAN_MB8
#define AT91C_BASE_CAN0_MB9 AT91C_BASE_CAN_MB9
#define AT91C_BASE_CAN0_MB10 AT91C_BASE_CAN_MB10
#define AT91C_BASE_CAN0_MB11 AT91C_BASE_CAN_MB11
#define AT91C_BASE_CAN0_MB12 AT91C_BASE_CAN_MB12
#define AT91C_BASE_CAN0_MB13 AT91C_BASE_CAN_MB13
#define AT91C_BASE_CAN0_MB14 AT91C_BASE_CAN_MB14
#define AT91C_BASE_CAN0_MB15 AT91C_BASE_CAN_MB15
#endif
#define NUM_MAILBOX_MAX 16
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
typedef struct
{
volatile unsigned char state;
volatile unsigned char can_number;
volatile unsigned char mailbox_number;
volatile unsigned char test_can;
volatile unsigned int mode_reg;
volatile unsigned int acceptance_mask_reg;
volatile unsigned int identifier;
volatile unsigned int data_low_reg;
volatile unsigned int data_high_reg;
volatile unsigned int control_reg;
volatile unsigned int mailbox_in_use;
volatile int size;
} CanTransfer;
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
extern unsigned char CAN_Init( unsigned int baudrate,
CanTransfer *canTransferRead,
CanTransfer *canTransferWrite );
extern void CAN_BasicTestSuite(void);
extern void CAN_disable( void );
extern void CAN_ResetAllMailbox( void );
extern void CAN_ResetTransfer( CanTransfer *pTransfer );
extern void CAN_InitMailboxRegisters( CanTransfer *pTransfer );
extern unsigned char CAN_IsInIdle( CanTransfer *pTransfer );
extern unsigned char CAN_Write( CanTransfer *pTransfer );
extern unsigned char CAN_Read( CanTransfer *pTransfer );
extern void CAN_BasicTestSuiteWithoutInterrupt( void );
extern unsigned char CAN_IsInIdle( CanTransfer *pTransfer );
#endif // _CAN_H

View File

@ -0,0 +1,156 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "dbgu.h"
#include <stdarg.h>
#include <board.h>
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Initializes the DBGU with the given parameters, and enables both the
/// transmitter and the receiver.
/// \param mode Operating mode to configure (see <Modes>).
/// \param baudrate Desired baudrate.
/// \param mck Frequency of the system master clock.
//------------------------------------------------------------------------------
void DBGU_Configure(unsigned int mode,
unsigned int baudrate,
unsigned int mck)
{
// Reset & disable receiver and transmitter, disable interrupts
AT91C_BASE_DBGU->DBGU_CR = AT91C_US_RSTRX | AT91C_US_RSTTX;
AT91C_BASE_DBGU->DBGU_IDR = 0xFFFFFFFF;
// Configure baud rate
AT91C_BASE_DBGU->DBGU_BRGR = mck / (baudrate * 16);
// Configure mode register
AT91C_BASE_DBGU->DBGU_MR = mode;
// Disable DMA channel
AT91C_BASE_DBGU->DBGU_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
// Enable receiver and transmitter
AT91C_BASE_DBGU->DBGU_CR = AT91C_US_RXEN | AT91C_US_TXEN;
}
//------------------------------------------------------------------------------
/// Outputs a character on the DBGU line.
/// \param c Character to send.
//------------------------------------------------------------------------------
static void DBGU_PutChar(unsigned char c)
{
// Wait for the transmitter to be ready
while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY) == 0);
// Send character
AT91C_BASE_DBGU->DBGU_THR = c;
// Wait for the transfer to complete
while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY) == 0);
}
//------------------------------------------------------------------------------
/// Reads and returns a character from the DBGU.
//------------------------------------------------------------------------------
unsigned char DBGU_GetChar()
{
while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_RXRDY) == 0);
return AT91C_BASE_DBGU->DBGU_RHR;
}
#ifndef NOFPUT
#include <stdio.h>
//------------------------------------------------------------------------------
/// Implementation of fputc using the DBGU as the standard output. Required
/// for printf().
/// Returns the character written if successful, or -1 if the output stream is
/// not stdout or stderr.
/// \param c Character to write.
/// \param pStream Output stream.
//------------------------------------------------------------------------------
signed int fputc(signed int c, FILE *pStream)
{
if ((pStream == stdout) || (pStream == stderr)) {
DBGU_PutChar(c);
return c;
}
else {
return EOF;
}
}
//------------------------------------------------------------------------------
/// Implementation of fputs using the DBGU as the standard output. Required
/// for printf(). Does NOT currently use the PDC.
/// Returns the number of characters written if successful, or -1 if the output
/// stream is not stdout or stderr.
/// \param pStr String to write.
/// \param pStream Output stream.
//------------------------------------------------------------------------------
signed int fputs(const char *pStr, FILE *pStream)
{
signed int num = 0;
while (*pStr != 0) {
if (fputc(*pStr, pStream) == -1) {
return -1;
}
num++;
pStr++;
}
return num;
}
#undef putchar
//------------------------------------------------------------------------------
/// Outputs a character on the DBGU. Returns the character itself.
/// \param c Character to output.
//------------------------------------------------------------------------------
signed int putchar(signed int c)
{
return fputc(c, stdout);
}
#endif //#ifndef NOFPUT

View File

@ -0,0 +1,72 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \dir
/// !Purpose
///
/// This module provides definitions and functions for using the DBGU.
///
/// !Usage
///
/// -# Enable the DBGU pins (see pio.h).
/// -# Configure the DBGU using DBGU_Configure.
///
/// \note Unless specified, all the functions defined here operate synchronously;
/// i.e. they all wait the data is sent/received before returning.
//------------------------------------------------------------------------------
#ifndef DBGU_H
#define DBGU_H
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page Modes
/// This page lists several common operating modes for the DBGU.
/// !Modes
/// - DBGU_STANDARD
/// Standard operating mode (asynchronous, 8bit, no parity)
#define DBGU_STANDARD AT91C_US_PAR_NONE
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
extern void DBGU_Configure(unsigned int mode,
unsigned int baudrate,
unsigned int mck);
extern unsigned char DBGU_GetChar();
#endif //#ifndef DBGU_H

View File

@ -0,0 +1,228 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 trace_LEVEL
#define trace_LEVEL trace_INFO
#endif
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "eefc.h"
#ifdef BOARD_FLASH_EEFC
#include <utility/assert.h>
#include <utility/trace.h>
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Enables the flash ready interrupt source on the EEFC peripheral.
//------------------------------------------------------------------------------
void EFC_EnableFrdyIt(void)
{
AT91C_BASE_EFC->EFC_FMR |= AT91C_EFC_FRDY;
}
//------------------------------------------------------------------------------
/// Disables the flash ready interrupt source on the EEFC peripheral.
//------------------------------------------------------------------------------
void EFC_DisableFrdyIt(void)
{
AT91C_BASE_EFC->EFC_FMR &= ~AT91C_EFC_FRDY;
}
//------------------------------------------------------------------------------
/// Translates the given address page and offset values. The resulting
/// values are stored in the provided variables if they are not null.
/// \param address Address to translate.
/// \param pPage First page accessed.
/// \param pOffset Byte offset in first page.
//------------------------------------------------------------------------------
void EFC_TranslateAddress(
unsigned int address,
unsigned short *pPage,
unsigned short *pOffset)
{
unsigned short page;
unsigned short offset;
SANITY_CHECK(address >= AT91C_IFLASH);
SANITY_CHECK(address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE));
// Calculate page & offset
page = (address - AT91C_IFLASH) / AT91C_IFLASH_PAGE_SIZE;
offset = (address - AT91C_IFLASH) % AT91C_IFLASH_PAGE_SIZE;
trace_LOG(trace_DEBUG,
"-D- Translated 0x%08X to page=%d and offset=%d\n\r",
address, page, offset);
// Store values
if (pPage) {
*pPage = page;
}
if (pOffset) {
*pOffset = offset;
}
}
//------------------------------------------------------------------------------
/// Computes the address of a flash access given the page and offset.
/// \param page Page number.
/// \param offset Byte offset inside page.
/// \param pAddress Computed address (optional).
//------------------------------------------------------------------------------
void EFC_ComputeAddress(
unsigned short page,
unsigned short offset,
unsigned int *pAddress)
{
unsigned int address;
SANITY_CHECK(page <= AT91C_IFLASH_NB_OF_PAGES);
SANITY_CHECK(offset < AT91C_IFLASH_PAGE_SIZE);
// Compute address
address = AT91C_IFLASH + page * AT91C_IFLASH_PAGE_SIZE + offset;
// Store result
if (pAddress) {
*pAddress = address;
}
}
//------------------------------------------------------------------------------
/// Starts the executing the given command on the EEFC. This function returns
/// as soon as the command is started. It does NOT set the FMCN field automatically.
/// \param command Command to execute.
/// \param argument Command argument (should be 0 if not used).
//------------------------------------------------------------------------------
void EFC_StartCommand(unsigned char command, unsigned short argument)
{
// Check command & argument
switch (command) {
case AT91C_EFC_FCMD_WP:
case AT91C_EFC_FCMD_WPL:
case AT91C_EFC_FCMD_EWP:
case AT91C_EFC_FCMD_EWPL:
case AT91C_EFC_FCMD_EPL:
case AT91C_EFC_FCMD_EPA:
case AT91C_EFC_FCMD_SLB:
case AT91C_EFC_FCMD_CLB:
ASSERT(argument < AT91C_IFLASH_NB_OF_PAGES,
"-F- Embedded flash has only %d pages\n\r",
AT91C_IFLASH_NB_OF_PAGES);
break;
case AT91C_EFC_FCMD_SFB:
case AT91C_EFC_FCMD_CFB:
ASSERT(argument < EFC_NUM_GPNVMS, "-F- Embedded flash has only %d GPNVMs\n\r", EFC_NUM_GPNVMS);
break;
case AT91C_EFC_FCMD_GETD:
case AT91C_EFC_FCMD_EA:
case AT91C_EFC_FCMD_GLB:
case AT91C_EFC_FCMD_GFB:
ASSERT(argument == 0, "-F- Argument is meaningless for the given command.\n\r");
break;
default: ASSERT(0, "-F- Unknown command %d\n\r", command);
}
// Start commandEmbedded flash
ASSERT((AT91C_BASE_EFC->EFC_FSR & AT91C_EFC_FRDY) == AT91C_EFC_FRDY, "-F- EEFC is not ready\n\r");
AT91C_BASE_EFC->EFC_FCR = (0x5A << 24) | (argument << 8) | command;
}
//------------------------------------------------------------------------------
/// Performs the given command and wait until its completion (or an error).
/// Returns 0 if successful; otherwise returns an error code.
/// \param command Command to perform.
/// \param argument Optional command argument.
//------------------------------------------------------------------------------
#ifdef __ICCARM__
__ramfunc
#else
__attribute__ ((section (".ramfunc")))
#endif
unsigned char EFC_PerformCommand(unsigned char command, unsigned short argument)
{
unsigned int status;
#ifdef BOARD_FLASH_IAP_ADDRESS
// Pointer on IAP function in ROM
static void (*IAP_PerformCommand)(unsigned int);
IAP_PerformCommand = (void (*)(unsigned int)) *((unsigned int *) BOARD_FLASH_IAP_ADDRESS);
// Check if IAP function is implemented (opcode in SWI != 'b' or 'ldr') */
if ((((((unsigned long) IAP_PerformCommand >> 24) & 0xFF) != 0xEA) &&
(((unsigned long) IAP_PerformCommand >> 24) & 0xFF) != 0xE5)) {
IAP_PerformCommand((0x5A << 24) | (argument << 8) | command);
return (AT91C_BASE_EFC->EFC_FSR & (AT91C_EFC_LOCKE | AT91C_EFC_FCMDE));
}
#endif
AT91C_BASE_EFC->EFC_FCR = (0x5A << 24) | (argument << 8) | command;
do {
status = AT91C_BASE_EFC->EFC_FSR;
}
while ((status & AT91C_EFC_FRDY) != AT91C_EFC_FRDY);
return (status & (AT91C_EFC_LOCKE | AT91C_EFC_FCMDE));
}
//------------------------------------------------------------------------------
/// Returns the current status of the EEFC. Keep in mind that this function clears
/// the value of some status bits (LOCKE, PROGE).
//------------------------------------------------------------------------------
unsigned int EFC_GetStatus(void)
{
return AT91C_BASE_EFC->EFC_FSR;
}
//------------------------------------------------------------------------------
/// Returns the result of the last executed command.
//------------------------------------------------------------------------------
unsigned int EFC_GetResult(void) {
return AT91C_BASE_EFC->EFC_FRR;
}
#endif //#ifdef BOARD_FLASH_EEFC

View File

@ -0,0 +1,133 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 EEFC_H
#define EEFC_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
#ifdef BOARD_FLASH_EEFC
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
/// Number of GPNVMs available on each chip.
#if defined(at91sam7l64) || defined(at91sam7l128)
#define EFC_NUM_GPNVMS 2
#elif defined(at91sam9xe128) || defined(at91sam9xe256) || defined(at91sam9xe512)
#define EFC_NUM_GPNVMS 17
#endif
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
// Needed when EEFC is integrated in MC.
#if !defined(AT91C_BASE_EFC) && defined(AT91C_BASE_MC)
typedef struct _AT91S_EFC {
AT91_REG EFC_FMR; // EFC Flash Mode Register
AT91_REG EFC_FCR; // EFC Flash Command Register
AT91_REG EFC_FSR; // EFC Flash Status Register
AT91_REG EFC_FRR; // EFC Flash Result Register
AT91_REG EFC_FVR; // EFC Flash Version Register
} AT91S_EFC, *AT91PS_EFC;
#define AT91C_EFC_FRDY AT91C_MC_FRDY
#define AT91C_EFC_FWS AT91C_MC_FWS
#define AT91C_EFC_FWS_0WS AT91C_MC_FWS_0WS
#define AT91C_EFC_FWS_1WS AT91C_MC_FWS_1WS
#define AT91C_EFC_FWS_2WS AT91C_MC_FWS_2WS
#define AT91C_EFC_FWS_3WS AT91C_MC_FWS_3WS
#define AT91C_EFC_FCMD AT91C_MC_FCMD
#define AT91C_EFC_FCMD_GETD AT91C_MC_FCMD_GETD
#define AT91C_EFC_FCMD_WP AT91C_MC_FCMD_WP
#define AT91C_EFC_FCMD_WPL AT91C_MC_FCMD_WPL
#define AT91C_EFC_FCMD_EWP AT91C_MC_FCMD_EWP
#define AT91C_EFC_FCMD_EWPL AT91C_MC_FCMD_EWPL
#define AT91C_EFC_FCMD_EA AT91C_MC_FCMD_EA
#define AT91C_EFC_FCMD_EPL AT91C_MC_FCMD_EPL
#define AT91C_EFC_FCMD_EPA AT91C_MC_FCMD_EPA
#define AT91C_EFC_FCMD_SLB AT91C_MC_FCMD_SLB
#define AT91C_EFC_FCMD_CLB AT91C_MC_FCMD_CLB
#define AT91C_EFC_FCMD_GLB AT91C_MC_FCMD_GLB
#define AT91C_EFC_FCMD_SFB AT91C_MC_FCMD_SFB
#define AT91C_EFC_FCMD_CFB AT91C_MC_FCMD_CFB
#define AT91C_EFC_FCMD_GFB AT91C_MC_FCMD_GFB
#define AT91C_EFC_FARG AT91C_MC_FARG
#define AT91C_EFC_FKEY AT91C_MC_FKEY
#define AT91C_EFC_FRDY_S AT91C_MC_FRDY_S
#define AT91C_EFC_FCMDE AT91C_MC_FCMDE
#define AT91C_EFC_LOCKE AT91C_MC_LOCKE
#define AT91C_EFC_FVALUE AT91C_MC_FVALUE
#define AT91C_BASE_EFC (AT91_CAST(AT91PS_EFC) 0xFFFFFF60)
#endif //#if !defined(AT91C_BASE_EFC) && defined(AT91C_BASE_MC)
//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------
extern void EFC_EnableFrdyIt(void);
extern void EFC_DisableFrdyIt(void);
extern void EFC_TranslateAddress(
unsigned int address,
unsigned short *pPage,
unsigned short *pOffset);
extern void EFC_ComputeAddress(
unsigned short page,
unsigned short offset,
unsigned int *pAddress);
extern void EFC_StartCommand(
unsigned char command,
unsigned short argument);
extern unsigned char EFC_PerformCommand(
unsigned char command,
unsigned short argument);
extern unsigned int EFC_GetStatus(void);
extern unsigned int EFC_GetResult(void);
#endif //#ifdef BOARD_FLASH_EEFC
#endif //#ifndef EEFC_H

View File

@ -0,0 +1,387 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 trace_LEVEL
#define trace_LEVEL trace_INFO
#endif
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "efc.h"
#ifdef BOARD_FLASH_EFC
#include <utility/assert.h>
#include <utility/trace.h>
//------------------------------------------------------------------------------
// Local definitions
//------------------------------------------------------------------------------
// Round a number to the nearest integral value (number must have been
// multiplied by 10, e.g. to round 10.3 enter 103).
#define ROUND(n) ((((n) % 10) >= 5) ? (((n) / 10) + 1) : ((n) / 10))
// Returns the FMCN field value when manipulating lock bits, given MCK.
#if defined(at91sam7a3)
#define FMCN_BITS(mck) (ROUND((mck) / 100000) << 16) // <- Not correct according to the datasheet but it works
#else
#define FMCN_BITS(mck) (ROUND((mck) / 100000) << 16)
#endif
// Returns the FMCN field value when manipulating the rest of the flash.
#define FMCN_FLASH(mck) ((((mck) / 2000000) * 3) << 16)
//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
/// Master clock frequency, used to infer the value of the FMCN field.
static unsigned int lMck;
/// Calculated value of the FMCN field base on Master clock frequency.
static unsigned int lMckFMCN;
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Sets the system master clock so the FMCN field of the EFC(s) can be
/// programmed properly.
/// \param mck Master clock frequency in Hz.
//------------------------------------------------------------------------------
void EFC_SetMasterClock(unsigned int mck)
{
lMck = mck;
lMckFMCN = FMCN_BITS(lMck);
}
//------------------------------------------------------------------------------
/// Enables the given interrupt sources on an EFC peripheral.
/// \param pEfc Pointer to an AT91S_EFC structure.
/// \param sources Interrupt sources to enable.
//------------------------------------------------------------------------------
void EFC_EnableIt(AT91S_EFC *pEfc, unsigned int sources)
{
SANITY_CHECK(pEfc);
SANITY_CHECK((sources & ~0x0000000D) == 0);
pEfc->EFC_FMR |= sources;
}
//------------------------------------------------------------------------------
/// Disables the given interrupt sources on an EFC peripheral.
/// \param pEfc Pointer to an AT91S_EFC structure.
/// \param sources Interrupt sources to disable.
//------------------------------------------------------------------------------
void EFC_DisableIt(AT91S_EFC *pEfc, unsigned int sources)
{
SANITY_CHECK(pEfc);
SANITY_CHECK((sources & ~(AT91C_MC_FRDY | AT91C_MC_LOCKE | AT91C_MC_PROGE)) == 0);
pEfc->EFC_FMR &= ~sources;
}
//------------------------------------------------------------------------------
/// Enables or disable the "Erase before programming" feature of an EFC.
/// \param pEfc Pointer to an AT91S_EFC structure.
/// \param enable If 1, the feature is enabled; otherwise it is disabled.
//------------------------------------------------------------------------------
void EFC_SetEraseBeforeProgramming(AT91S_EFC *pEfc, unsigned char enable)
{
SANITY_CHECK(pEfc);
if (enable) {
pEfc->EFC_FMR &= ~AT91C_MC_NEBP;
}
else {
pEfc->EFC_FMR |= AT91C_MC_NEBP;
}
}
//------------------------------------------------------------------------------
/// Translates the given address into EFC, page and offset values. The resulting
/// values are stored in the provided variables if they are not null.
/// \param address Address to translate.
/// \param ppEfc Pointer to target EFC peripheral.
/// \param pPage First page accessed.
/// \param pOffset Byte offset in first page.
//------------------------------------------------------------------------------
void EFC_TranslateAddress(
unsigned int address,
AT91S_EFC **ppEfc,
unsigned short *pPage,
unsigned short *pOffset)
{
AT91S_EFC *pEfc;
unsigned short page;
unsigned short offset;
SANITY_CHECK(address >= AT91C_IFLASH);
SANITY_CHECK(address <= (AT91C_IFLASH + AT91C_IFLASH_SIZE));
#if defined(AT91C_BASE_EFC0)
if (address >= (AT91C_IFLASH + AT91C_IFLASH_SIZE / 2)) {
pEfc = AT91C_BASE_EFC1;
page = (address - AT91C_IFLASH - AT91C_IFLASH_SIZE / 2) / AT91C_IFLASH_PAGE_SIZE;
offset = (address - AT91C_IFLASH - AT91C_IFLASH_SIZE / 2) % AT91C_IFLASH_PAGE_SIZE;
}
else {
pEfc = AT91C_BASE_EFC0;
page = (address - AT91C_IFLASH) / AT91C_IFLASH_PAGE_SIZE;
offset = (address - AT91C_IFLASH) % AT91C_IFLASH_PAGE_SIZE;
}
#else
pEfc = AT91C_BASE_EFC;
page = (address - AT91C_IFLASH) / AT91C_IFLASH_PAGE_SIZE;
offset = (address - AT91C_IFLASH) % AT91C_IFLASH_PAGE_SIZE;
#endif
trace_LOG(trace_DEBUG,
"-D- Translated 0x%08X to EFC=0x%08X, page=%d and offset=%d\n\r",
address, (unsigned int) pEfc, page, offset);
// Store values
if (ppEfc) {
*ppEfc = pEfc;
}
if (pPage) {
*pPage = page;
}
if (pOffset) {
*pOffset = offset;
}
}
//------------------------------------------------------------------------------
/// Computes the address of a flash access given the EFC, page and offset.
/// \param pEfc Pointer to an AT91S_EFC structure.
/// \param page Page number.
/// \param offset Byte offset inside page.
/// \param pAddress Computed address (optional).
//------------------------------------------------------------------------------
void EFC_ComputeAddress(
AT91S_EFC *pEfc,
unsigned short page,
unsigned short offset,
unsigned int *pAddress)
{
unsigned int address;
SANITY_CHECK(pEfc);
#if defined(AT91C_BASE_EFC1)
SANITY_CHECK(page <= (AT91C_IFLASH_NB_OF_PAGES / 2));
#else
SANITY_CHECK(page <= AT91C_IFLASH_NB_OF_PAGES);
#endif
SANITY_CHECK(offset < AT91C_IFLASH_PAGE_SIZE);
// Compute address
address = AT91C_IFLASH + page * AT91C_IFLASH_PAGE_SIZE + offset;
#if defined(AT91C_BASE_EFC1)
if (pEfc == AT91C_BASE_EFC1) {
address += AT91C_IFLASH_SIZE / 2;
}
#endif
// Store result
if (pAddress) {
*pAddress = address;
}
}
//------------------------------------------------------------------------------
/// Starts the executing the given command on an EFC. This function returns
/// as soon as the command is started. It does NOT set the FMCN field automatically.
/// \param pEfc Pointer to an AT91S_EFC structure.
/// \param command Command to execute.
/// \param argument Command argument (should be 0 if not used).
//------------------------------------------------------------------------------
void EFC_StartCommand(
AT91S_EFC *pEfc,
unsigned char command,
unsigned short argument)
{
SANITY_CHECK(pEfc);
ASSERT(lMck != 0, "-F- Master clock not set.\n\r");
// Check command & argument
switch (command) {
case AT91C_MC_FCMD_PROG_AND_LOCK:
ASSERT(0, "-F- Write and lock command cannot be carried out.\n\r");
break;
case AT91C_MC_FCMD_START_PROG:
case AT91C_MC_FCMD_LOCK:
case AT91C_MC_FCMD_UNLOCK:
ASSERT(argument < AT91C_IFLASH_NB_OF_PAGES,
"-F- Maximum number of pages is %d (argument was %d)\n\r",
AT91C_IFLASH_NB_OF_PAGES,
argument);
break;
#if (EFC_NUM_GPNVMS > 0)
case AT91C_MC_FCMD_SET_GP_NVM:
case AT91C_MC_FCMD_CLR_GP_NVM:
ASSERT(argument < EFC_NUM_GPNVMS, "-F- A maximum of %d GPNVMs are available on the chip.\n\r", EFC_NUM_GPNVMS);
break;
#endif
case AT91C_MC_FCMD_ERASE_ALL:
#if !defined(EFC_NO_SECURITY_BIT)
case AT91C_MC_FCMD_SET_SECURITY:
#endif
ASSERT(argument == 0, "-F- Argument is meaningless for the given command\n\r");
break;
default: ASSERT(0, "-F- Unknown command %d\n\r", command);
}
// Set FMCN
switch (command) {
case AT91C_MC_FCMD_LOCK:
case AT91C_MC_FCMD_UNLOCK:
#if (EFC_NUM_GPNVMS > 0)
case AT91C_MC_FCMD_SET_GP_NVM:
case AT91C_MC_FCMD_CLR_GP_NVM:
#endif
#if !defined(EFC_NO_SECURITY_BIT)
case AT91C_MC_FCMD_SET_SECURITY:
#endif
pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;
break;
case AT91C_MC_FCMD_START_PROG:
case AT91C_MC_FCMD_ERASE_ALL:
pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;
break;
}
// Start command
ASSERT((pEfc->EFC_FSR & AT91C_MC_FRDY) != 0, "-F- Efc is not ready\n\r");
pEfc->EFC_FCR = (0x5A << 24) | (argument << 8) | command;
}
//------------------------------------------------------------------------------
/// Performs the given command and wait until its completion (or an error).
/// Returns 0 if successful; otherwise returns an error code.
/// \param pEfc Pointer to an AT91S_EFC structure.
/// \param command Command to perform.
/// \param argument Optional command argument.
//------------------------------------------------------------------------------
#ifdef __ICCARM__
__ramfunc
#else
__attribute__ ((section (".ramfunc")))
#endif
unsigned char EFC_PerformCommand(
AT91S_EFC *pEfc,
unsigned char command,
unsigned short argument)
{
unsigned int status;
// Set FMCN
switch (command) {
case AT91C_MC_FCMD_LOCK:
case AT91C_MC_FCMD_UNLOCK:
#if (EFC_NUM_GPNVMS > 0)
case AT91C_MC_FCMD_SET_GP_NVM:
case AT91C_MC_FCMD_CLR_GP_NVM:
#endif
#if !defined(EFC_NO_SECURITY_BIT)
case AT91C_MC_FCMD_SET_SECURITY:
#endif
pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;
break;
case AT91C_MC_FCMD_START_PROG:
case AT91C_MC_FCMD_ERASE_ALL:
pEfc->EFC_FMR = (pEfc->EFC_FMR & ~AT91C_MC_FMCN) | lMckFMCN;
break;
}
#ifdef BOARD_FLASH_IAP_ADDRESS
// Pointer on IAP function in ROM
static void (*IAP_PerformCommand)(unsigned int, unsigned int);
unsigned int index = 0;
#ifdef AT91C_BASE_EFC1
if (pEfc == AT91C_BASE_EFC1) {
index = 1;
}
#endif
IAP_PerformCommand = (void (*)(unsigned int, unsigned int)) *((unsigned int *) BOARD_FLASH_IAP_ADDRESS);
// Check if IAP function is implemented (opcode in SWI != 'b' or 'ldr') */
if ((((((unsigned long) IAP_PerformCommand >> 24) & 0xFF) != 0xEA) &&
(((unsigned long) IAP_PerformCommand >> 24) & 0xFF) != 0xE5)) {
IAP_PerformCommand(index, (0x5A << 24) | (argument << 8) | command);
return (pEfc->EFC_FSR & (AT91C_MC_LOCKE | AT91C_MC_PROGE));
}
#endif
pEfc->EFC_FCR = (0x5A << 24) | (argument << 8) | command;
do {
status = pEfc->EFC_FSR;
}
while ((status & AT91C_MC_FRDY) == 0);
return (status & (AT91C_MC_PROGE | AT91C_MC_LOCKE));
}
//------------------------------------------------------------------------------
/// Returns the current status of an EFC. Keep in mind that this function clears
/// the value of some status bits (LOCKE, PROGE).
/// \param pEfc Pointer to an AT91S_EFC structure.
//------------------------------------------------------------------------------
unsigned int EFC_GetStatus(AT91S_EFC *pEfc)
{
return pEfc->EFC_FSR;
}
#endif //#ifdef BOARD_FLASH_EFC

View File

@ -0,0 +1,127 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 EFC_H
#define EFC_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
#ifdef BOARD_FLASH_EFC
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
/// Number of GPNVMs available on each chip.
#if defined(at91sam7s16) || defined(at91sam7s161) || defined(at91sam7s32) \
|| defined(at91sam7s321) || defined(at91sam7s64) || defined(at91sam7s128) \
|| defined(at91sam7s256) || defined(at91sam7s512)
#define EFC_NUM_GPNVMS 2
#elif defined(at91sam7se32) || defined(at91sam7se256) || defined(at91sam7se512) \
|| defined(at91sam7x128) || defined(at91sam7x256) || defined(at91sam7x512) \
|| defined(at91sam7xc128) || defined(at91sam7xc256) || defined(at91sam7xc512) \
#define EFC_NUM_GPNVMS 3
#elif defined(at91sam7a3)
#define EFC_NUM_GPNVMS 0
#endif
// Missing FRDY bit for SAM7A3
#if defined(at91sam7a3)
#define AT91C_MC_FRDY (AT91C_MC_EOP | AT91C_MC_EOL)
#endif
// No security bit on SAM7A3
#if defined(at91sam7a3)
#define EFC_NO_SECURITY_BIT
#endif
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
// For chips which do not define AT91S_EFC
#if !defined(AT91C_BASE_EFC) && !defined(AT91C_BASE_EFC0)
typedef struct _AT91S_EFC {
AT91_REG EFC_FMR;
AT91_REG EFC_FCR;
AT91_REG EFC_FSR;
} AT91S_EFC, *AT91PS_EFC;
#define AT91C_BASE_EFC (AT91_CAST(AT91PS_EFC) 0xFFFFFF60)
#endif
//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------
extern void EFC_SetMasterClock(unsigned int mck);
extern void EFC_EnableIt(AT91S_EFC *pEfc, unsigned int sources);
extern void EFC_DisableIt(AT91S_EFC *pEfc, unsigned int sources);
extern void EFC_SetEraseBeforeProgramming(AT91S_EFC *pEfc, unsigned char enable);
extern void EFC_TranslateAddress(
unsigned int address,
AT91S_EFC **ppEfc,
unsigned short *pPage,
unsigned short *pOffset);
extern void EFC_ComputeAddress(
AT91S_EFC *pEfc,
unsigned short page,
unsigned short offset,
unsigned int *pAddress);
extern void EFC_StartCommand(
AT91S_EFC *pEfc,
unsigned char command,
unsigned short argument);
extern unsigned char EFC_PerformCommand(
AT91S_EFC *pEfc,
unsigned char command,
unsigned short argument);
extern unsigned int EFC_GetStatus(AT91S_EFC *pEfc);
#endif //#ifdef BOARD_FLASH_EFC
#endif //#ifndef EFC_H

View File

@ -0,0 +1,833 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//-----------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------
#include <board.h>
#include "emac.h"
#include <utility/trace.h>
#include <utility/assert.h>
#include <string.h>
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
/// The buffer addresses written into the descriptors must be aligned so the
/// last few bits are zero. These bits have special meaning for the EMAC
/// peripheral and cannot be used as part of the address.
#define EMAC_ADDRESS_MASK ((unsigned int)0xFFFFFFFC)
#define EMAC_LENGTH_FRAME ((unsigned int)0x0FFF) /// Length of frame mask
// receive buffer descriptor bits
#define EMAC_RX_OWNERSHIP_BIT (1UL << 0)
#define EMAC_RX_WRAP_BIT (1UL << 1)
#define EMAC_RX_SOF_BIT (1UL << 14)
#define EMAC_RX_EOF_BIT (1UL << 15)
// Transmit buffer descriptor bits
#define EMAC_TX_LAST_BUFFER_BIT (1UL << 15)
#define EMAC_TX_WRAP_BIT (1UL << 30)
#define EMAC_TX_USED_BIT (1UL << 31)
//-----------------------------------------------------------------------------
// Circular buffer management
//-----------------------------------------------------------------------------
// Return count in buffer
#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))
// Return space available, 0..size-1
// We always leave one free char as a completely full buffer
// has head == tail, which is the same as empty
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
// Return count up to the end of the buffer.
// Carefully avoid accessing head and tail more than once,
// so they can change underneath us without returning inconsistent results
#define CIRC_CNT_TO_END(head,tail,size) \
({int end = (size) - (tail); \
int n = ((head) + end) & ((size)-1); \
n < end ? n : end;})
// Return space available up to the end of the buffer
#define CIRC_SPACE_TO_END(head,tail,size) \
({int end = (size) - 1 - (head); \
int n = (end + (tail)) & ((size)-1); \
n <= end ? n : end+1;})
// Increment head or tail
#define CIRC_INC(headortail,size) \
headortail++; \
if(headortail >= size) { \
headortail = 0; \
}
#define CIRC_EMPTY(circ) ((circ)->head == (circ)->tail)
#define CIRC_CLEAR(circ) ((circ)->head = (circ)->tail = 0)
//------------------------------------------------------------------------------
// Structures
//------------------------------------------------------------------------------
#ifdef __ICCARM__ // IAR
#pragma pack(4) // IAR
#define __attribute__(...) // IAR
#endif // IAR
/// Describes the type and attribute of Receive Transfer descriptor.
typedef struct _EmacRxTDescriptor {
unsigned int addr;
unsigned int status;
} __attribute__((packed, aligned(8))) EmacRxTDescriptor, *PEmacRxTDescriptor;
/// Describes the type and attribute of Transmit Transfer descriptor.
typedef struct _EmacTxTDescriptor {
unsigned int addr;
unsigned int status;
} __attribute__((packed, aligned(8))) EmacTxTDescriptor, *PEmacTxTDescriptor;
#ifdef __ICCARM__ // IAR
#pragma pack() // IAR
#endif // IAR
/// Descriptors for RX (required aligned by 8)
typedef struct {
volatile EmacRxTDescriptor td[RX_BUFFERS];
EMAC_RxCallback rxCb; /// Callback function to be invoked once a frame has been received
unsigned short idx;
} RxTd;
/// Descriptors for TX (required aligned by 8)
typedef struct {
volatile EmacTxTDescriptor td[TX_BUFFERS];
EMAC_TxCallback txCb[TX_BUFFERS]; /// Callback function to be invoked once TD has been processed
EMAC_WakeupCallback wakeupCb; /// Callback function to be invoked once several TD have been released
unsigned short wakeupThreshold; /// Number of free TD before wakeupCb is invoked
unsigned short head; /// Circular buffer head pointer incremented by the upper layer (buffer to be sent)
unsigned short tail; /// Circular buffer head pointer incremented by the IT handler (buffer sent)
} TxTd;
//------------------------------------------------------------------------------
// Internal variables
//------------------------------------------------------------------------------
// Receive Transfer Descriptor buffer
#ifdef __ICCARM__ // IAR
#pragma data_alignment=8 // IAR
#endif // IAR
static volatile RxTd rxTd;
// Transmit Transfer Descriptor buffer
#ifdef __ICCARM__ // IAR
#pragma data_alignment=8 // IAR
#endif // IAR
static volatile TxTd txTd;
/// Send Buffer
// Section 3.6 of AMBA 2.0 spec states that burst should not cross 1K Boundaries.
// Receive buffer manager writes are burst of 2 words => 3 lsb bits of the address shall be set to 0
#ifdef __ICCARM__ // IAR
#pragma data_alignment=8 // IAR
#endif // IAR
static volatile unsigned char pTxBuffer[TX_BUFFERS * EMAC_TX_UNITSIZE] __attribute__((aligned(8)));
#ifdef __ICCARM__ // IAR
#pragma data_alignment=8 // IAR
#endif // IAR
/// Receive Buffer
static volatile unsigned char pRxBuffer[RX_BUFFERS * EMAC_RX_UNITSIZE] __attribute__((aligned(8)));
/// Statistics
static volatile EmacStats EmacStatistics;
//-----------------------------------------------------------------------------
// Internal functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// Wait PHY operation complete.
/// Return 1 if the operation completed successfully.
/// May be need to re-implemented to reduce CPU load.
/// \param retry: the retry times, 0 to wait forever until complete.
//-----------------------------------------------------------------------------
static unsigned char EMAC_WaitPhy( unsigned int retry )
{
unsigned int retry_count = 0;
while((AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE) == 0) {
// Dead LOOP!
if (retry == 0) {
continue;
}
// Timeout check
retry_count++;
if(retry_count >= retry) {
trace_LOG(trace_ERROR, "E: Wait PHY time out\n\r");
return 0;
}
}
return 1;
}
//-----------------------------------------------------------------------------
// Exported functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PHY management functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// Set MDC clock according to current board clock. Per 802.3, MDC should be
/// less then 2.5MHz.
/// Return 1 if successfully, 0 if MDC clock not found.
//-----------------------------------------------------------------------------
unsigned char EMAC_SetMdcClock( unsigned int mck )
{
int clock_dividor;
if (mck <= 20000000) {
clock_dividor = AT91C_EMAC_CLK_HCLK_8; /// MDC clock = MCK/8
}
else if (mck <= 40000000) {
clock_dividor = AT91C_EMAC_CLK_HCLK_16; /// MDC clock = MCK/16
}
else if (mck <= 80000000) {
clock_dividor = AT91C_EMAC_CLK_HCLK_32; /// MDC clock = MCK/32
}
else if (mck <= 160000000) {
clock_dividor = AT91C_EMAC_CLK_HCLK_64; /// MDC clock = MCK/64
}
else {
trace_LOG(trace_ERROR, "E: No valid MDC clock.\n\r");
return 0;
}
AT91C_BASE_EMAC->EMAC_NCFGR = (AT91C_BASE_EMAC->EMAC_NCFGR & (~AT91C_EMAC_CLK))
| clock_dividor;
return 1;
}
//-----------------------------------------------------------------------------
/// Enable MDI with PHY
//-----------------------------------------------------------------------------
void EMAC_EnableMdio( void )
{
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE;
}
//-----------------------------------------------------------------------------
/// Enable MDI with PHY
//-----------------------------------------------------------------------------
void EMAC_DisableMdio( void )
{
AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE;
}
//-----------------------------------------------------------------------------
/// Enable MII mode for EMAC, called once after autonegotiate
//-----------------------------------------------------------------------------
void EMAC_EnableMII( void )
{
AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN;
}
//-----------------------------------------------------------------------------
/// Enable RMII mode for EMAC, called once after autonegotiate
//-----------------------------------------------------------------------------
void EMAC_EnableRMII( void )
{
AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN | AT91C_EMAC_RMII;
}
//-----------------------------------------------------------------------------
/// Read PHY register.
/// Return 1 if successfully, 0 if timeout.
/// \param PhyAddress PHY Address
/// \param Address Register Address
/// \param pValue Pointer to a 32 bit location to store read data
/// \param retry The retry times, 0 to wait forever until complete.
//-----------------------------------------------------------------------------
unsigned char EMAC_ReadPhy(unsigned char PhyAddress,
unsigned char Address,
unsigned int *pValue,
unsigned int retry)
{
AT91C_BASE_EMAC->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30))
| (AT91C_EMAC_CODE & (2 << 16))
| (AT91C_EMAC_RW & (2 << 28))
| (AT91C_EMAC_PHYA & ((PhyAddress & 0x1f) << 23))
| (AT91C_EMAC_REGA & (Address << 18));
if ( EMAC_WaitPhy(retry) == 0 ) {
trace_LOG(trace_ERROR, "TimeOut EMAC_ReadPhy\n\r");
return 0;
}
*pValue = ( AT91C_BASE_EMAC->EMAC_MAN & 0x0000ffff );
return 1;
}
//-----------------------------------------------------------------------------
/// Write PHY register
/// Return 1 if successfully, 0 if timeout.
/// \param PhyAddress PHY Address
/// \param Address Register Address
/// \param Value Data to write ( Actually 16 bit data )
/// \param retry The retry times, 0 to wait forever until complete.
//-----------------------------------------------------------------------------
unsigned char EMAC_WritePhy(unsigned char PhyAddress,
unsigned char Address,
unsigned int Value,
unsigned int retry)
{
AT91C_BASE_EMAC->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30))
| (AT91C_EMAC_CODE & (2 << 16))
| (AT91C_EMAC_RW & (1 << 28))
| (AT91C_EMAC_PHYA & ((PhyAddress & 0x1f) << 23))
| (AT91C_EMAC_REGA & (Address << 18))
| (AT91C_EMAC_DATA & Value) ;
if ( EMAC_WaitPhy(retry) == 0 ) {
trace_LOG(trace_ERROR, "TimeOut EMAC_WritePhy\n\r");
return 0;
}
return 1;
}
//-----------------------------------------------------------------------------
/// Setup the EMAC for the link : speed 100M/10M and Full/Half duplex
/// \param speed Link speed, 0 for 10M, 1 for 100M
/// \param fullduplex 1 for Full Duplex mode
//-----------------------------------------------------------------------------
void EMAC_SetLinkSpeed(unsigned char speed, unsigned char fullduplex)
{
unsigned int ncfgr;
ncfgr = AT91C_BASE_EMAC->EMAC_NCFGR;
ncfgr &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
if (speed) {
ncfgr |= AT91C_EMAC_SPD;
}
if (fullduplex) {
ncfgr |= AT91C_EMAC_FD;
}
AT91C_BASE_EMAC->EMAC_NCFGR = ncfgr;
}
//-----------------------------------------------------------------------------
// EMAC functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// EMAC Interrupt handler
//-----------------------------------------------------------------------------
void EMAC_Handler(void)
{
volatile EmacTxTDescriptor *pTxTd;
volatile EMAC_TxCallback *pTxCb;
unsigned int isr;
unsigned int rsr;
unsigned int tsr;
unsigned int rxStatusFlag;
unsigned int txStatusFlag;
//trace_LOG(trace_DEBUG, "EMAC_Handler\n\r");
isr = AT91C_BASE_EMAC->EMAC_ISR & AT91C_BASE_EMAC->EMAC_IMR;
rsr = AT91C_BASE_EMAC->EMAC_RSR;
tsr = AT91C_BASE_EMAC->EMAC_TSR;
// RX packet
if ((isr & AT91C_EMAC_RCOMP) || (rsr & AT91C_EMAC_REC)) {
rxStatusFlag = AT91C_EMAC_REC;
// Frame received
EmacStatistics.rx_packets++;
// Check OVR
if (rsr & AT91C_EMAC_OVR) {
rxStatusFlag |= AT91C_EMAC_OVR;
EmacStatistics.rx_ovrs++;
}
// Check BNA
if (rsr & AT91C_EMAC_BNA) {
rxStatusFlag |= AT91C_EMAC_BNA;
EmacStatistics.rx_bnas++;
}
// Clear status
AT91C_BASE_EMAC->EMAC_RSR |= rxStatusFlag;
// Invoke callbacks
if (rxTd.rxCb) {
rxTd.rxCb(rxStatusFlag);
}
}
// TX packet
if ((isr & AT91C_EMAC_TCOMP) || (tsr & AT91C_EMAC_COMP)) {
txStatusFlag = AT91C_EMAC_COMP;
EmacStatistics.tx_comp ++;
// A frame transmitted
// Check RLE
if (tsr & AT91C_EMAC_RLES) {
txStatusFlag |= AT91C_EMAC_RLES;
EmacStatistics.tx_errors++;
}
// Check COL
if (tsr & AT91C_EMAC_COL) {
txStatusFlag |= AT91C_EMAC_COL;
EmacStatistics.collisions++;
}
// Check BEX
if (tsr & AT91C_EMAC_BEX) {
txStatusFlag |= AT91C_EMAC_BEX;
EmacStatistics.tx_exausts++;
}
// Check UND
if (tsr & AT91C_EMAC_UND) {
txStatusFlag |= AT91C_EMAC_UND;
EmacStatistics.tx_underruns++;
}
// Clear status
AT91C_BASE_EMAC->EMAC_TSR |= txStatusFlag;
// Sanity check: Tx buffers have to be scheduled
ASSERT(!CIRC_EMPTY(&txTd),
"-F- EMAC Tx interrupt received meanwhile no TX buffers has been scheduled\n\r");
// Check the buffers
while (CIRC_CNT(txTd.head, txTd.tail, TX_BUFFERS)) {
pTxTd = txTd.td + txTd.tail;
pTxCb = txTd.txCb + txTd.tail;
// Exit if buffer has not been sent yet
if ((pTxTd->status & EMAC_TX_USED_BIT) == 0) {
break;
}
// Notify upper layer that packet has been sent
if (*pTxCb) {
(*pTxCb)(txStatusFlag);
}
CIRC_INC( txTd.tail, TX_BUFFERS );
}
// If a wakeup has been scheduled, notify upper layer that it can send
// other packets, send will be successfull.
if( (CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) >= txTd.wakeupThreshold)
&& txTd.wakeupCb) {
txTd.wakeupCb();
}
}
}
//-----------------------------------------------------------------------------
/// Initialize the EMAC with the emac controller address
/// \param id HW ID for power management
/// \param pTxWakeUpfct Thresold TX Wakeup Callback
/// \param pRxfct RX Wakeup Callback
/// \param pMacAddress Mac Address
/// \param enableCAF enable AT91C_EMAC_CAF if needed by application
/// \param enableNBC AT91C_EMAC_NBC if needed by application
//-----------------------------------------------------------------------------
void EMAC_Init( unsigned char id, const unsigned char *pMacAddress,
unsigned char enableCAF, unsigned char enableNBC )
{
int Index;
unsigned int Address;
// Check parameters
ASSERT(RX_BUFFERS * EMAC_RX_UNITSIZE > EMAC_FRAME_LENTGH_MAX,
"E: RX buffers too small\n\r");
trace_LOG(trace_DEBUG, "EMAC_Init\n\r");
// Power ON
AT91C_BASE_PMC->PMC_PCER = 1 << id;
// Disable TX & RX and more
AT91C_BASE_EMAC->EMAC_NCR = 0;
// disable
AT91C_BASE_EMAC->EMAC_IDR = ~0;
rxTd.idx = 0;
CIRC_CLEAR(&txTd);
// Setup the RX descriptors.
for(Index = 0; Index < RX_BUFFERS; Index++) {
Address = (unsigned int)(&(pRxBuffer[Index * EMAC_RX_UNITSIZE]));
// Remove EMAC_RX_OWNERSHIP_BIT and EMAC_RX_WRAP_BIT
rxTd.td[Index].addr = Address & EMAC_ADDRESS_MASK;
rxTd.td[Index].status = 0;
}
rxTd.td[RX_BUFFERS - 1].addr |= EMAC_RX_WRAP_BIT;
// Setup the TX descriptors.
for(Index = 0; Index < TX_BUFFERS; Index++) {
Address = (unsigned int)(&(pTxBuffer[Index * EMAC_TX_UNITSIZE]));
txTd.td[Index].addr = Address;
txTd.td[Index].status = EMAC_TX_USED_BIT;
}
txTd.td[TX_BUFFERS - 1].status = EMAC_TX_USED_BIT | EMAC_TX_WRAP_BIT;
// Set the MAC address
if( pMacAddress != (unsigned char *)0 ) {
AT91C_BASE_EMAC->EMAC_SA1L = ( ((unsigned int)pMacAddress[3] << 24)
| ((unsigned int)pMacAddress[2] << 16)
| ((unsigned int)pMacAddress[1] << 8 )
| pMacAddress[0] );
AT91C_BASE_EMAC->EMAC_SA1H = ( ((unsigned int)pMacAddress[5] << 8 )
| pMacAddress[4] );
}
// Now setup the descriptors
// Receive Buffer Queue Pointer Register
AT91C_BASE_EMAC->EMAC_RBQP = (unsigned int) (rxTd.td);
// Transmit Buffer Queue Pointer Register
AT91C_BASE_EMAC->EMAC_TBQP = (unsigned int) (txTd.td);
AT91C_BASE_EMAC->EMAC_NCR = AT91C_EMAC_CLRSTAT;
// Clear all status bits in the receive status register.
AT91C_BASE_EMAC->EMAC_RSR = (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
// Clear all status bits in the transmit status register
AT91C_BASE_EMAC->EMAC_TSR = ( AT91C_EMAC_UBR | AT91C_EMAC_COL | AT91C_EMAC_RLES
| AT91C_EMAC_BEX | AT91C_EMAC_COMP
| AT91C_EMAC_UND );
// Clear interrupts
AT91C_BASE_EMAC->EMAC_ISR;
// Enable the copy of data into the buffers
// ignore broadcasts, and don't copy FCS.
AT91C_BASE_EMAC->EMAC_NCFGR |= (AT91C_EMAC_DRFCS | AT91C_EMAC_PAE);
if( enableCAF == EMAC_CAF_ENABLE ) {
AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_CAF;
}
if( enableNBC == EMAC_NBC_ENABLE ) {
AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_NBC;
}
// Enable Rx and Tx, plus the stats register.
AT91C_BASE_EMAC->EMAC_NCR |= (AT91C_EMAC_TE | AT91C_EMAC_RE | AT91C_EMAC_WESTAT);
// Setup the interrupts for TX (and errors)
AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RXUBR
| AT91C_EMAC_TUNDR
| AT91C_EMAC_RLEX
| AT91C_EMAC_TXERR
| AT91C_EMAC_TCOMP
| AT91C_EMAC_ROVR
| AT91C_EMAC_HRESP;
}
//-----------------------------------------------------------------------------
/// Get the statstic information & reset it
/// \param pStats Pointer to EmacStats structure to copy the informations
/// \param reset Reset the statistics after copy it
//-----------------------------------------------------------------------------
void EMAC_GetStatistics(EmacStats *pStats, unsigned char reset)
{
unsigned int ncrBackup = 0;
trace_LOG(trace_DEBUG, "EMAC_GetStatistics\n\r");
// Sanity check
if (pStats == (EmacStats *) 0) {
return;
}
ncrBackup = AT91C_BASE_EMAC->EMAC_NCR & (AT91C_EMAC_TE | AT91C_EMAC_RE);
// Disable TX/RX
AT91C_BASE_EMAC->EMAC_NCR = ncrBackup & ~(AT91C_EMAC_TE | AT91C_EMAC_RE);
// Copy the informations
memcpy(pStats, (void*)&EmacStatistics, sizeof(EmacStats));
// Reset the statistics
if (reset) {
memset((void*)&EmacStatistics, 0x00, sizeof(EmacStats));
AT91C_BASE_EMAC->EMAC_NCR = ncrBackup | AT91C_EMAC_CLRSTAT;
}
// restore NCR
AT91C_BASE_EMAC->EMAC_NCR = ncrBackup;
}
//-----------------------------------------------------------------------------
/// Send a packet with EMAC.
/// If the packet size is larger than transfer buffer size error returned.
/// \param buffer The buffer to be send
/// \param size The size of buffer to be send
/// \param fEMAC_TxCallback Threshold Wakeup callback
/// \param fWakeUpCb TX Wakeup
/// \return OK, Busy or invalid packet
//-----------------------------------------------------------------------------
unsigned char EMAC_Send(void *pBuffer,
unsigned int size,
EMAC_TxCallback fEMAC_TxCallback)
{
volatile EmacTxTDescriptor *pTxTd;
volatile EMAC_TxCallback *pTxCb;
//trace_LOG(trace_DEBUG, "EMAC_Send\n\r");
// Check parameter
if (size > EMAC_TX_UNITSIZE) {
trace_LOG(trace_ERROR, "-E- EMAC driver does not split send packets.");
trace_LOG(trace_ERROR, " It can send %d bytes max in one packet (%u bytes requested)\n\r",
EMAC_TX_UNITSIZE, size);
return EMAC_TX_INVALID_PACKET;
}
// If no free TxTd, buffer can't be sent, schedule the wakeup callback
if( CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) == 0) {
return EMAC_TX_BUFFER_BUSY;
}
// Pointers to the current TxTd
pTxTd = txTd.td + txTd.head;
pTxCb = txTd.txCb + txTd.head;
// Sanity check
ASSERT((pTxTd->status & EMAC_TX_USED_BIT) != 0,
"-F- Buffer is still under EMAC control\n\r");
// Setup/Copy data to transmition buffer
if (pBuffer && size) {
// Driver manage the ring buffer
memcpy((void *)pTxTd->addr, pBuffer, size);
}
// Tx Callback
*pTxCb = fEMAC_TxCallback;
// Update TD status
// The buffer size defined is length of ethernet frame
// so it's always the last buffer of the frame.
if (txTd.head == TX_BUFFERS-1) {
pTxTd->status =
(size & EMAC_LENGTH_FRAME) | EMAC_TX_LAST_BUFFER_BIT | EMAC_TX_WRAP_BIT;
}
else {
pTxTd->status = (size & EMAC_LENGTH_FRAME) | EMAC_TX_LAST_BUFFER_BIT;
}
CIRC_INC(txTd.head, TX_BUFFERS)
// Tx packets count
EmacStatistics.tx_packets++;
// Now start to transmit if it is not already done
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;
return EMAC_TX_OK;
}
//-----------------------------------------------------------------------------
/// Receive a packet with EMAC
/// If not enough buffer for the packet, the remaining data is lost but right
/// frame length is returned.
/// \param pFrame Buffer to store the frame
/// \param frameSize Size of the frame
/// \param pRcvSize Received size
/// \return OK, no data, or frame too small
//-----------------------------------------------------------------------------
unsigned char EMAC_Poll(unsigned char *pFrame,
unsigned int frameSize,
unsigned int *pRcvSize)
{
unsigned short bufferLength;
unsigned int tmpFrameSize=0;
unsigned char *pTmpFrame=0;
unsigned int tmpIdx = rxTd.idx;
volatile EmacRxTDescriptor *pRxTd = rxTd.td + rxTd.idx;
ASSERT(pFrame, "F: EMAC_Poll\n\r");
char isFrame = 0;
// Set the default return value
*pRcvSize = 0;
// Process received RxTd
while ((pRxTd->addr & EMAC_RX_OWNERSHIP_BIT) == EMAC_RX_OWNERSHIP_BIT) {
// A start of frame has been received, discard previous fragments
if ((pRxTd->status & EMAC_RX_SOF_BIT) == EMAC_RX_SOF_BIT) {
// Skip previous fragment
while (tmpIdx != rxTd.idx) {
pRxTd = rxTd.td + rxTd.idx;
pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
CIRC_INC(rxTd.idx, RX_BUFFERS);
}
// Reset the temporary frame pointer
pTmpFrame = pFrame;
tmpFrameSize = 0;
// Start to gather buffers in a frame
isFrame = 1;
}
// Increment the pointer
CIRC_INC(tmpIdx, RX_BUFFERS);
// Copy data in the frame buffer
if (isFrame) {
if (tmpIdx == rxTd.idx) {
trace_LOG(trace_INFO,
"I: no EOF (Invalid of buffers too small)\n\r");
do {
pRxTd = rxTd.td + rxTd.idx;
pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
CIRC_INC(rxTd.idx, RX_BUFFERS);
} while(tmpIdx != rxTd.idx);
return EMAC_RX_NO_DATA;
}
// Copy the buffer into the application frame
bufferLength = EMAC_RX_UNITSIZE;
if ((tmpFrameSize + bufferLength) > frameSize) {
bufferLength = frameSize - tmpFrameSize;
}
memcpy(pTmpFrame, (void*)(pRxTd->addr & EMAC_ADDRESS_MASK), bufferLength);
pTmpFrame += bufferLength;
tmpFrameSize += bufferLength;
// An end of frame has been received, return the data
if ((pRxTd->status & EMAC_RX_EOF_BIT) == EMAC_RX_EOF_BIT) {
// Frame size from the EMAC
*pRcvSize = (pRxTd->status & EMAC_LENGTH_FRAME);
// Application frame buffer is too small all data have not been copied
if (tmpFrameSize < *pRcvSize) {
printf("size req %u size allocated %u\n\r", *pRcvSize, frameSize);
return EMAC_RX_FRAME_SIZE_TOO_SMALL;
}
trace_LOG(trace_INFO, "packet %d-%u (%u)\n\r", rxTd.idx, tmpIdx, *pRcvSize);
// All data have been copied in the application frame buffer => release TD
while (rxTd.idx != tmpIdx) {
pRxTd = rxTd.td + rxTd.idx;
pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
CIRC_INC(rxTd.idx, RX_BUFFERS);
}
EmacStatistics.rx_packets++;
return EMAC_RX_OK;
}
}
// SOF has not been detected, skip the fragment
else {
pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
rxTd.idx = tmpIdx;
}
// Process the next buffer
pRxTd = rxTd.td + tmpIdx;
}
//trace_LOG(trace_DEBUG, "E");
return EMAC_RX_NO_DATA;
}
//-----------------------------------------------------------------------------
/// Registers pRxCb callback. Callback will be invoked after the next received
/// frame.
/// When EMAC_Poll() returns EMAC_RX_NO_DATA the application task call EMAC_Set_RxCb()
/// to register pRxCb() callback and enters suspend state. The callback is in charge
/// to resume the task once a new frame has been received. The next time EMAC_Poll()
/// is called, it will be successfull.
/// \param pRxCb Pointer to callback function
//-----------------------------------------------------------------------------
void EMAC_Set_RxCb(EMAC_RxCallback pRxCb)
{
rxTd.rxCb = pRxCb;
AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP;
}
//-----------------------------------------------------------------------------
/// Remove the RX callback function.
/// This function is usually invoked from the RX callback itself. Once the callback
/// has resumed the application task, there is no need to invoke the callback again.
//-----------------------------------------------------------------------------
void EMAC_Clear_RxCb(void)
{
AT91C_BASE_EMAC->EMAC_IDR = AT91C_EMAC_RCOMP;
rxTd.rxCb = (EMAC_RxCallback) 0;
}
//-----------------------------------------------------------------------------
/// Registers TX wakeup callback callback. Callback will be invoked once several
/// transfer descriptors are available.
/// When EMAC_Send() returns EMAC_TX_BUFFER_BUSY (all TD busy) the application
/// task calls EMAC_Set_TxWakeUpCb() to register pTxWakeUpCb() callback and
/// enters suspend state. The callback is in charge to resume the task once
/// several TD have been released. The next time EMAC_Send() will be called, it
/// shall be successfull.
/// \param pTxWakeUpCb Pointer to callback function
/// \param threshold Minimum number of available transfer descriptors before pTxWakeUpCb() is invoked
/// \return 0= success, 1 = threshold exceeds nuber of transfer descriptors
//-----------------------------------------------------------------------------
char EMAC_Set_TxWakeUpCb(EMAC_WakeupCallback pTxWakeUpCb, unsigned short threshold)
{
if (threshold <= TX_BUFFERS) {
txTd.wakeupCb = pTxWakeUpCb;
txTd.wakeupThreshold = threshold;
return 0;
}
return 1;
}
//-----------------------------------------------------------------------------
/// Remove the TX wakeup callback function.
/// This function is usually invoked from the TX wakeup callback itself. Once the callback
/// has resumed the application task, there is no need to invoke the callback again.
//-----------------------------------------------------------------------------
void EMAC_Clear_TxWakeUpCb(void)
{
txTd.wakeupCb = (EMAC_WakeupCallback) 0;
}

View File

@ -0,0 +1,161 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
// peripherals/emac/emac.h
#ifndef EMAC_H
#define EMAC_H
//-----------------------------------------------------------------------------
/// \dir
/// !Purpose
///
/// Definition of methods and structures for using EMAC
///
/// !Usage
///
/// -# Initialize EMAC with EMAC_Init.
/// -# Setup EMAC with EMAC_SetupTx, EMAC_SetupRx, EMAC_SetupMacAddress
/// and EMAC_SetupStack.
/// -# Drive the EMAC status machine by EMAC_Task.
/// -# EMAC_GetStatus give EMAC status machine current status
/// -# Send a packet to network with EMAC_SendPacket.
/// -# Get a packet from network with EMAC_GetPacket.
///
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Definitions
//-----------------------------------------------------------------------------
/// Number of buffer for RX, be carreful: MUST be 2^n
#define RX_BUFFERS 16
/// Number of buffer for TX, be carreful: MUST be 2^n
#define TX_BUFFERS 8
/// Buffer Size
#define EMAC_RX_UNITSIZE 128 /// Fixed size for RX buffer
#define EMAC_TX_UNITSIZE 1518 /// Size for ETH frame length
// The MAC can support frame lengths up to 1536 bytes.
#define EMAC_FRAME_LENTGH_MAX 1536
//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// Describes the statistics of the EMAC.
//-----------------------------------------------------------------------------
typedef struct _EmacStats {
// TX errors
unsigned int tx_packets; /// Total Number of packets sent
unsigned int tx_comp; /// Packet complete
unsigned int tx_errors; /// TX errors ( Retry Limit Exceed )
unsigned int collisions; /// Collision
unsigned int tx_exausts; /// Buffer exhausted
unsigned int tx_underruns; /// Under Run, not able to read from memory
// RX errors
unsigned int rx_packets; /// Total Number of packets RX
unsigned int rx_eof; /// No EOF error
unsigned int rx_ovrs; /// Over Run, not able to store to memory
unsigned int rx_bnas; /// Buffer is not available
} EmacStats, *PEmacStats;
//-----------------------------------------------------------------------------
// PHY Exported functions
//-----------------------------------------------------------------------------
extern unsigned char EMAC_SetMdcClock( unsigned int mck );
extern void EMAC_EnableMdio( void );
extern void EMAC_DisableMdio( void );
extern void EMAC_EnableMII( void );
extern void EMAC_EnableRMII( void );
extern unsigned char EMAC_ReadPhy(unsigned char PhyAddress,
unsigned char Address,
unsigned int *pValue,
unsigned int retry);
extern unsigned char EMAC_WritePhy(unsigned char PhyAddress,
unsigned char Address,
unsigned int Value,
unsigned int retry);
extern void EMAC_SetLinkSpeed(unsigned char speed,
unsigned char fullduplex);
//-----------------------------------------------------------------------------
// EMAC Exported functions
//-----------------------------------------------------------------------------
/// Callback used by send function
typedef void (*EMAC_TxCallback)(unsigned int status);
typedef void (*EMAC_RxCallback)(unsigned int status);
typedef void (*EMAC_WakeupCallback)(void);
extern void EMAC_Init( unsigned char id, const unsigned char *pMacAddress,
unsigned char enableCAF, unsigned char enableNBC );
#define EMAC_CAF_DISABLE 0
#define EMAC_CAF_ENABLE 1
#define EMAC_NBC_DISABLE 0
#define EMAC_NBC_ENABLE 1
extern void EMAC_Handler(void);
extern unsigned char EMAC_Send(void *pBuffer,
unsigned int size,
EMAC_TxCallback fEMAC_TxCallback);
/// Return for EMAC_Send function
#define EMAC_TX_OK 0
#define EMAC_TX_BUFFER_BUSY 1
#define EMAC_TX_INVALID_PACKET 2
extern unsigned char EMAC_Poll(unsigned char *pFrame,
unsigned int frameSize,
unsigned int *pRcvSize);
/// Return for EMAC_Poll function
#define EMAC_RX_OK 0
#define EMAC_RX_NO_DATA 1
#define EMAC_RX_FRAME_SIZE_TOO_SMALL 2
extern void EMAC_GetStatistics(EmacStats *pStats, unsigned char reset);
#endif // #ifndef EMAC_H

View File

@ -0,0 +1,362 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "lcd.h"
#include <board.h>
#include <utility/assert.h>
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Enables the LCD controller, after waiting for the specified number of
/// frames.
/// \param frames Number of frames before the LCD is enabled.
//------------------------------------------------------------------------------
void LCD_Enable(unsigned int frames)
{
ASSERT((frames & 0xFFFFFF80) == 0,
"LCD_Enable: Wrong frames value.\n\r");
AT91C_BASE_LCDC->LCDC_PWRCON = AT91C_LCDC_PWR | (frames << 1);
}
//------------------------------------------------------------------------------
/// Disables the LCD controller, after waiting for the specified number of
/// frames.
/// \param frames Number of frames before the LCD is shut down.
//------------------------------------------------------------------------------
void LCD_Disable(unsigned int frames)
{
ASSERT((frames & 0xFFFFFF80) == 0,
"LCD_Disable: Wrong frames value.\n\r");
AT91C_BASE_LCDC->LCDC_PWRCON = frames << 1;
}
//------------------------------------------------------------------------------
/// Enables the DMA of the LCD controller.
//------------------------------------------------------------------------------
void LCD_EnableDma()
{
AT91C_BASE_LCDC->LCDC_DMACON = AT91C_LCDC_DMAEN;
}
//------------------------------------------------------------------------------
/// Disables the DMA of the LCD controller.
//------------------------------------------------------------------------------
void LCD_DisableDma()
{
AT91C_BASE_LCDC->LCDC_DMACON = 0;
}
//------------------------------------------------------------------------------
/// Configures the internal clock of the LCD controller given the master clock of
/// the system and the desired pixel clock in MHz.
/// \param masterClock Master clock frequency.
/// \param pixelClock Pixel clock frequency.
//------------------------------------------------------------------------------
void LCD_SetPixelClock(unsigned int masterClock, unsigned int pixelClock)
{
AT91C_BASE_LCDC->LCDC_LCDCON1 = ((masterClock / (2 * pixelClock)) - 1) << 12;
}
//------------------------------------------------------------------------------
/// Sets the type of display used with the LCD controller.
/// \param displayType Type of display used.
//------------------------------------------------------------------------------
void LCD_SetDisplayType(unsigned int displayType)
{
unsigned int value;
ASSERT((displayType & ~AT91C_LCDC_DISTYPE) == 0,
"LCD_SetDisplayType: Wrong display type value.\n\r");
value = AT91C_BASE_LCDC->LCDC_LCDCON2;
value &= ~AT91C_LCDC_DISTYPE;
value |= displayType;
AT91C_BASE_LCDC->LCDC_LCDCON2 = value;
}
//------------------------------------------------------------------------------
/// Sets the scan mode used by the LCD (either single scan or double-scan).
/// \param scanMode Scan mode to use.
//------------------------------------------------------------------------------
void LCD_SetScanMode(unsigned int scanMode)
{
unsigned int value;
ASSERT((scanMode & ~AT91C_LCDC_SCANMOD) == 0,
"LCD_SetScanMode: Wrong scan mode value.\n\r");
value = AT91C_BASE_LCDC->LCDC_LCDCON2;
value &= ~AT91C_LCDC_SCANMOD;
value |= scanMode;
AT91C_BASE_LCDC->LCDC_LCDCON2 = value;
}
//------------------------------------------------------------------------------
/// Sets the number of bits per pixel used by the LCD display.
/// \param bitsPerPixel Number of bits per pixel to use.
//------------------------------------------------------------------------------
void LCD_SetBitsPerPixel(unsigned int bitsPerPixel)
{
unsigned int value;
ASSERT((bitsPerPixel & ~AT91C_LCDC_PIXELSIZE) == 0,
"LCD_SetScanMode: Wrong bitsPerPixel value.\n\r");
value = AT91C_BASE_LCDC->LCDC_LCDCON2;
value &= ~AT91C_LCDC_PIXELSIZE;
value |= bitsPerPixel;
AT91C_BASE_LCDC->LCDC_LCDCON2 = value;
}
//------------------------------------------------------------------------------
/// Sets the LCDD, LCDVSYNC, LCDHSYNC, LCDDOTCLK and LCDDEN signal polarities.
/// \param lcdd LCDD signal polarity.
/// \param lcdvsync LCDVSYNC signal polarity.
/// \param lcdhsync LCDHSYNC signal polarity.
/// \param lcddotclk LCDDOTCLK signal polarity.
/// \param lcdden LCDDEN signal polarity.
//------------------------------------------------------------------------------
void LCD_SetPolarities(
unsigned int lcdd,
unsigned int lcdvsync,
unsigned int lcdhsync,
unsigned int lcddotclk,
unsigned int lcdden)
{
unsigned int value;
ASSERT((lcdd & ~AT91C_LCDC_INVVD) == 0,
"LCD_SetPolarities: Wrong lcdd value.\n\r");
ASSERT((lcdvsync & ~AT91C_LCDC_INVFRAME) == 0,
"LCD_SetPolarities: Wrong lcdvsync value.\n\r");
ASSERT((lcdhsync & ~AT91C_LCDC_INVLINE) == 0,
"LCD_SetPolarities: Wrong lcdhsync value.\n\r");
ASSERT((lcddotclk & ~AT91C_LCDC_INVCLK) == 0,
"LCD_SetPolarities: Wrong lcddotclk value.\n\r");
ASSERT((lcdden & ~AT91C_LCDC_INVDVAL) == 0,
"LCD_SetPolarities: Wrong lcdden value.\n\r");
value = AT91C_BASE_LCDC->LCDC_LCDCON2;
value &= 0xFFFFE0FF;
value |= lcdd | lcdvsync | lcdhsync | lcddotclk | lcdden;
AT91C_BASE_LCDC->LCDC_LCDCON2 = value;
}
//------------------------------------------------------------------------------
/// Sets the LCD clock mode, i.e. always active or active only during display
/// period.
/// \param clockMode Clock mode to use.
//------------------------------------------------------------------------------
void LCD_SetClockMode(unsigned int clockMode)
{
unsigned int value;
ASSERT((clockMode & ~AT91C_LCDC_CLKMOD) == 0,
"LCD_SetScanMode: Wrong scan mode value.\n\r");
value = AT91C_BASE_LCDC->LCDC_LCDCON2;
value &= ~AT91C_LCDC_CLKMOD;
value |= clockMode;
AT91C_BASE_LCDC->LCDC_LCDCON2 = value;
}
//------------------------------------------------------------------------------
/// Sets the format of the frame buffer memory.
/// \param format Memory ordering format.
//------------------------------------------------------------------------------
void LCD_SetMemoryFormat(unsigned int format)
{
unsigned int value;
ASSERT((format & ~AT91C_LCDC_MEMOR) == 0,
"LCD_SetMemoryFormat: Wrong memory format value.\n\r");
value = AT91C_BASE_LCDC->LCDC_LCDCON2;
value &= ~AT91C_LCDC_MEMOR;
value |= format;
AT91C_BASE_LCDC->LCDC_LCDCON2 = value;
}
//------------------------------------------------------------------------------
/// Sets the size in pixel of the LCD display.
/// \param width Width in pixel of the LCD display.
/// \param height Height in pixel of the LCD display.
//------------------------------------------------------------------------------
void LCD_SetSize(unsigned int width, unsigned int height)
{
ASSERT(((width - 1) & 0xFFFFF800) == 0,
"LCD_SetSize: Wrong width value.\n\r");
ASSERT(((height - 1) & 0xFFFFF800) == 0,
"LCD_SetSize: Wrong height value.\n\r");
AT91C_BASE_LCDC->LCDC_LCDFRCFG = ((width - 1) << 21) | (height - 1);
}
//------------------------------------------------------------------------------
/// Sets the vertical timings of the LCD controller. Only meaningful when
/// using a TFT display.
/// \param vfp Number of idle lines at the end of a frame.
/// \param vbp Number of idle lines at the beginning of a frame.
/// \param vpw Vertical synchronization pulse width in number of lines.
/// \param vhdly Delay between LCDVSYNC edge and LCDHSYNC rising edge, in
/// LCDDOTCLK cycles.
//------------------------------------------------------------------------------
void LCD_SetVerticalTimings(
unsigned int vfp,
unsigned int vbp,
unsigned int vpw,
unsigned int vhdly)
{
ASSERT((vfp & 0xFFFFFF00) == 0,
"LCD_SetVerticalTimings: Wrong vfp value.\n\r");
ASSERT((vbp & 0xFFFFFF00) == 0,
"LCD_SetVerticalTimings: Wrong vbp value.\n\r");
ASSERT(((vpw-1) & 0xFFFFFFC0) == 0,
"LCD_SetVerticalTimings: Wrong vpw value.\n\r");
ASSERT(((vhdly-1) & 0xFFFFFFF0) == 0,
"LCD_SetVerticalTimings: Wrong vhdly value.\n\r");
AT91C_BASE_LCDC->LCDC_TIM1 = vfp
| (vbp << 8)
| ((vpw-1) << 16)
| ((vhdly-1) << 24);
}
//------------------------------------------------------------------------------
/// Sets the horizontal timings of the LCD controller. Meaningful for both
/// STN and TFT displays.
/// \param hbp Number of idle LCDDOTCLK cycles at the beginning of a line.
/// \param hpw Width of the LCDHSYNC pulse, in LCDDOTCLK cycles.
/// \param hfp Number of idel LCDDOTCLK cycles at the end of a line.
//------------------------------------------------------------------------------
void LCD_SetHorizontalTimings(
unsigned int hbp,
unsigned int hpw,
unsigned int hfp)
{
ASSERT(((hbp-1) & 0xFFFFFF00) == 0,
"LCD_SetHorizontalTimings: Wrong hbp value.\n\r");
ASSERT(((hpw-1) & 0xFFFFFFC0) == 0,
"LCD_SetHorizontalTimings: Wrong hpw value.\n\r");
ASSERT(((hfp-1) & 0xFFFFFF00) == 0,
"LCD_SetHorizontalTimings: Wrong hfp value.\n\r");
AT91C_BASE_LCDC->LCDC_TIM2 = (hbp-1) | ((hpw-1) << 8) | ((hfp-1) << 24);
}
//------------------------------------------------------------------------------
/// Sets the address of the frame buffer in the LCD controller DMA. When using
/// dual-scan mode, this is the upper frame buffer.
/// \param address Frame buffer address.
//------------------------------------------------------------------------------
void LCD_SetFrameBufferAddress(void *address)
{
AT91C_BASE_LCDC->LCDC_BA1 = (unsigned int) address;
}
//------------------------------------------------------------------------------
/// Sets the size in pixels of a frame (height * width * bpp).
/// \param frameSize Size of frame in pixels.
//------------------------------------------------------------------------------
void LCD_SetFrameSize(unsigned int frameSize)
{
ASSERT((frameSize & 0xFF800000) == 0,
"LCD_SetFrameSize: Wrong frameSize value.\n\r");
AT91C_BASE_LCDC->LCDC_FRMCFG = frameSize | (AT91C_BASE_LCDC->LCDC_FRMCFG & 0xFF000000);
}
//------------------------------------------------------------------------------
/// Sets the DMA controller burst length.
/// \param burstLength Desired burst length.
//------------------------------------------------------------------------------
void LCD_SetBurstLength(unsigned int burstLength)
{
ASSERT(((burstLength-1) & 0xFFFFFF80) == 0,
"LCD_SetBurstLength: Wrong burstLength value.\n\r");
AT91C_BASE_LCDC->LCDC_FRMCFG &= 0x00FFFFFF;
AT91C_BASE_LCDC->LCDC_FRMCFG |= ((burstLength-1) << 24);
AT91C_BASE_LCDC->LCDC_FIFO = 2048 - (2 * burstLength + 3);
}
//------------------------------------------------------------------------------
/// Sets the prescaler value of the contrast control PWM.
/// \param prescaler Desired prescaler value.
//------------------------------------------------------------------------------
void LCD_SetContrastPrescaler(unsigned int prescaler)
{
ASSERT((prescaler & ~AT91C_LCDC_PS) == 0,
"LCD_SetContrastPrescaler: Wrong prescaler value\n\r");
AT91C_BASE_LCDC->LCDC_CTRSTCON &= ~AT91C_LCDC_PS;
AT91C_BASE_LCDC->LCDC_CTRSTCON |= prescaler;
}
//------------------------------------------------------------------------------
/// Sets the polarity of the contrast PWM.
/// \param polarity PWM polarity
//------------------------------------------------------------------------------
void LCD_SetContrastPolarity(unsigned int polarity)
{
ASSERT((polarity & ~AT91C_LCDC_POL) == 0,
"LCD_SetContrastPolarity: Wrong polarity value\n\r");
AT91C_BASE_LCDC->LCDC_CTRSTCON &= ~AT91C_LCDC_POL;
AT91C_BASE_LCDC->LCDC_CTRSTCON |= polarity;
}
//------------------------------------------------------------------------------
/// Sets the threshold value of the constrast PWM.
/// \param value PWM threshold value.
//------------------------------------------------------------------------------
void LCD_SetContrastValue(unsigned int value)
{
ASSERT((value & ~AT91C_LCDC_CVAL) == 0,
"LCD_SetContrastValue: Wrong value.\n\r");
AT91C_BASE_LCDC->LCDC_CTRSTVAL = value;
}
//------------------------------------------------------------------------------
/// Enables the contrast PWM generator.
//------------------------------------------------------------------------------
void LCD_EnableContrast()
{
AT91C_BASE_LCDC->LCDC_CTRSTCON |= AT91C_LCDC_ENA_PWMGEMENABLED;
}

View File

@ -0,0 +1,92 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 LCD_H
#define LCD_H
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
extern void LCD_Enable(unsigned int frames);
extern void LCD_Disable(unsigned int frames);
extern void LCD_EnableDma();
extern void LCD_DisableDma();
extern void LCD_SetPixelClock(unsigned int masterClock, unsigned int pixelClock);
extern void LCD_SetDisplayType(unsigned int displayType);
extern void LCD_SetScanMode(unsigned int scanMode);
extern void LCD_SetBitsPerPixel(unsigned int bitsPerPixel);
extern void LCD_SetPolarities(
unsigned int lcdd,
unsigned int lcdvsync,
unsigned int lcdhsync,
unsigned int lcddotclk,
unsigned int lcdden);
extern void LCD_SetClockMode(unsigned int clockMode);
extern void LCD_SetMemoryFormat(unsigned int format);
extern void LCD_SetSize(unsigned int width, unsigned int height);
extern void LCD_SetVerticalTimings(
unsigned int vfp,
unsigned int vbp,
unsigned int vpw,
unsigned int vhdly);
extern void LCD_SetHorizontalTimings(
unsigned int hbp,
unsigned int hpw,
unsigned int hfp);
extern void LCD_SetFrameBufferAddress(void *address);
extern void LCD_SetFrameSize(unsigned int frameSize);
extern void LCD_SetBurstLength(unsigned int burstLength);
extern void LCD_SetContrastPrescaler(unsigned int prescaler);
extern void LCD_SetContrastPolarity(unsigned int polarity);
extern void LCD_SetContrastValue(unsigned int value);
extern void LCD_EnableContrast();
#endif //#ifndef LCD_H

View File

@ -0,0 +1,551 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support - ROUSSET -
* ----------------------------------------------------------------------------
* Copyright (c) 2006, Atmel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the disclaimer below in the documentation and/or
* other materials provided with the distribution.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "mci.h"
#include <utility/assert.h>
#include <utility/trace.h>
//------------------------------------------------------------------------------
// Local constants
//------------------------------------------------------------------------------
/// Bit mask for status register errors.
#define STATUS_ERRORS (AT91C_MCI_UNRE \
| AT91C_MCI_OVRE \
| AT91C_MCI_DTOE \
| AT91C_MCI_DCRCE \
| AT91C_MCI_RTOE \
| AT91C_MCI_RENDE \
| AT91C_MCI_RCRCE \
| AT91C_MCI_RDIRE \
| AT91C_MCI_RINDE)
/// MCI data timeout configuration with 1048576 MCK cycles between 2 data transfers.
#define DTOR_1MEGA_CYCLES (AT91C_MCI_DTOCYC | AT91C_MCI_DTOMUL)
#define SDCARD_APP_OP_COND_CMD (41 | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_TRCMD_NO )
#define MMC_SEND_OP_COND_CMD (1 | AT91C_MCI_TRCMD_NO | AT91C_MCI_SPCMD_NONE | AT91C_MCI_RSPTYP_48 | AT91C_MCI_OPDCMD)
#define DISABLE 0 // Disable MCI interface
#define ENABLE 1 // Enable MCI interface
//------------------------------------------------------------------------------
// Local macros
//------------------------------------------------------------------------------
/// Used to write in PMC registers.
#define WRITE_PMC(pPmc, regName, value) pPmc->regName = (value)
/// Used to write in MCI registers.
#define WRITE_MCI(pMci, regName, value) pMci->regName = (value)
/// Used to read from MCI registers.
#define READ_MCI(pMci, regName) (pMci->regName)
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Enable/disable a MCI driver instance.
/// \param pMci Pointer to a MCI driver instance.
/// \param enb 0 for disable MCI and 1 for enable MCI.
//------------------------------------------------------------------------------
void MCI_Enable(Mci *pMci, unsigned char enb)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
SANITY_CHECK(pMci);
SANITY_CHECK(pMci->pMciHw);
// Set the Control Register: Enable/Disable MCI interface clock
if(enb == DISABLE) {
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS);
}
else {
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN);
}
}
//------------------------------------------------------------------------------
/// Initializes a MCI driver instance and the underlying peripheral.
/// \param pMci Pointer to a MCI driver instance.
/// \param pMciHw Pointer to a MCI peripheral.
/// \param mciId MCI peripheral identifier.
/// \param mode Slot and type of connected card.
//------------------------------------------------------------------------------
void MCI_Init(
Mci *pMci,
AT91S_MCI *pMciHw,
unsigned char mciId,
unsigned int mode)
{
unsigned short clkDiv;
SANITY_CHECK(pMci);
SANITY_CHECK(pMciHw);
SANITY_CHECK((mode == MCI_MMC_SLOTA) || (mode == MCI_MMC_SLOTB)
|| (mode == MCI_SD_SLOTA) || (mode == MCI_SD_SLOTB));
// Initialize the MCI driver structure
pMci->pMciHw = pMciHw;
pMci->mciId = mciId;
pMci->semaphore = 1;
pMci->pCommand = 0;
// Enable the MCI clock
WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << mciId));
// Reset the MCI
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST);
// Disable the MCI
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS);
// Disable all the interrupts
WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF);
// Set the Data Timeout Register
WRITE_MCI(pMciHw, MCI_DTOR, DTOR_1MEGA_CYCLES);
// Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58)
clkDiv = (BOARD_MCK / (400000 * 2)) - 1;
WRITE_MCI(pMciHw, MCI_MR, (clkDiv | (AT91C_MCI_PWSDIV & (0x7 << 8))));
// Set the SDCard Register
WRITE_MCI(pMciHw, MCI_SDCR, mode);
// Enable the MCI and the Power Saving
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIEN);
// Disable the MCI peripheral clock.
WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << mciId));
}
//------------------------------------------------------------------------------
/// Close a MCI driver instance and the underlying peripheral.
/// \param pMci Pointer to a MCI driver instance.
/// \param pMciHw Pointer to a MCI peripheral.
/// \param mciId MCI peripheral identifier.
//------------------------------------------------------------------------------
void MCI_Close(Mci *pMci)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
SANITY_CHECK(pMci);
SANITY_CHECK(pMciHw);
// Initialize the MCI driver structure
pMci->semaphore = 1;
pMci->pCommand = 0;
// Disable the MCI peripheral clock.
WRITE_PMC(AT91C_BASE_PMC, PMC_PCDR, (1 << pMci->mciId));
// Disable the MCI
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS);
// Disable all the interrupts
WRITE_MCI(pMciHw, MCI_IDR, 0xFFFFFFFF);
}
//------------------------------------------------------------------------------
/// Configure the MCI CLKDIV in the MCI_MR register. The max. for MCI clock is
/// MCK/2 and corresponds to CLKDIV = 0
/// \param pMci Pointer to the low level MCI driver.
/// \param mciSpeed MCI clock speed in Hz.
//------------------------------------------------------------------------------
void MCI_SetSpeed(Mci *pMci, unsigned int mciSpeed)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
unsigned int mciMr;
unsigned short clkdiv;
SANITY_CHECK(pMci);
SANITY_CHECK(pMci->pMciHw);
// Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58)
mciMr = READ_MCI(pMciHw, MCI_MR) & (~AT91C_MCI_CLKDIV);
// Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK)
// divided by (2*(CLKDIV+1))
if (mciSpeed > 0) {
clkdiv = (BOARD_MCK / (mciSpeed * 2));
if (clkdiv > 0) {
clkdiv -= 1;
}
}
else {
clkdiv = 0;
}
WRITE_MCI(pMciHw, MCI_MR, mciMr | clkdiv);
}
//------------------------------------------------------------------------------
/// Configure the MCI SDCBUS in the MCI_SDCR register. Only two modes available
///
/// \param pMci Pointer to the low level MCI driver.
/// \param busWidth MCI bus width mode.
//------------------------------------------------------------------------------
void MCI_SetBusWidth(Mci *pMci, unsigned char busWidth)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
unsigned int mciSdcr;
SANITY_CHECK(pMci);
SANITY_CHECK(pMci->pMciHw);
mciSdcr = (READ_MCI(pMciHw, MCI_SDCR) & ~(AT91C_MCI_SCDBUS));
WRITE_MCI(pMciHw, MCI_SDCR, mciSdcr | busWidth);
}
//------------------------------------------------------------------------------
/// Starts a MCI transfer. This is a non blocking function. It will return
/// as soon as the transfer is started.
/// Return 0 if successful; otherwise returns MCI_ERROR_LOCK if the driver is
/// already in use.
/// \param pMci Pointer to an MCI driver instance.
/// \param pCommand Pointer to the command to execute.
//------------------------------------------------------------------------------
unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pCommand)
{
AT91PS_MCI pMciHw = pMci->pMciHw;
unsigned int mciIer, mciMr;
SANITY_CHECK(pMci);
SANITY_CHECK(pMciHw);
SANITY_CHECK(pCommand);
// Try to acquire the MCI semaphore
if (pMci->semaphore == 0) {
return MCI_ERROR_LOCK;
}
pMci->semaphore--;
// trace_LOG(trace_DEBUG, "MCI_SendCommand %x %d\n\r", READ_MCI(pMciHw, MCI_SR), pCommand->cmd & 0x3f);
// Command is now being executed
pMci->pCommand = pCommand;
pCommand->status = MCI_STATUS_PENDING;
// Enable the MCI clock
WRITE_PMC(AT91C_BASE_PMC, PMC_PCER, (1 << pMci->mciId));
//Disable MCI clock, for multi-block data transfer
MCI_Enable(pMci, DISABLE);
// Set PDC data transfer direction
if(pCommand->blockSize > 0) {
if(pCommand->isRead) {
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN);
}
else {
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN);
}
}
// Disable transmitter and receiver
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS);
mciMr = READ_MCI(pMciHw, MCI_MR) & (~(AT91C_MCI_BLKLEN | AT91C_MCI_PDCMODE));
// Command with DATA stage
if (pCommand->blockSize > 0) {
// Enable PDC mode and set block size
if(pCommand->conTrans != MCI_CONTINUE_TRANSFER) {
WRITE_MCI(pMciHw, MCI_MR, mciMr | AT91C_MCI_PDCMODE | (pCommand->blockSize << 16));
}
// DATA transfer from card to host
if (pCommand->isRead) {
WRITE_MCI(pMciHw, MCI_RPR, (int) pCommand->pData);
// If Multiblock command set the BLKR register
/* if (pCommand->nbBlock > 1) {
WRITE_MCI(pMciHw, MCI_BLKR, pCommand->nbBlock | (pCommand->blockSize << 16));
}
else {
WRITE_MCI(pMciHw, MCI_BLKR, (pCommand->blockSize << 16));
}*/
// Sanity check
if (pCommand->nbBlock == 0)
pCommand->nbBlock = 1;
////////
if ((pCommand->blockSize & 0x3) != 0) {
WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4 + 1);
}
else {
WRITE_MCI(pMciHw, MCI_RCR, (pCommand->nbBlock * pCommand->blockSize) / 4);
}
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_RXTEN);
mciIer = AT91C_MCI_ENDRX | STATUS_ERRORS;
// mciIer = AT91C_MCI_RXBUFF | STATUS_ERRORS;
}
// DATA transfer from host to card
else {
// Sanity check
if (pCommand->nbBlock == 0)
pCommand->nbBlock = 1;
WRITE_MCI(pMciHw, MCI_TPR, (int) pCommand->pData);
// Update the PDC counter
if ((pCommand->blockSize & 0x3) != 0) {
WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4 + 1);
}
else {
WRITE_MCI(pMciHw, MCI_TCR, (pCommand->nbBlock * pCommand->blockSize) / 4);
}
// MCI_BLKE notifies the end of Multiblock command
mciIer = AT91C_MCI_BLKE | STATUS_ERRORS;
}
}
// No data transfer: stop at the end of the command
else {
WRITE_MCI(pMciHw, MCI_MR, mciMr);
mciIer = AT91C_MCI_CMDRDY | STATUS_ERRORS;
}
// Enable MCI clock
MCI_Enable(pMci, ENABLE);
// Send the command
if((pCommand->conTrans != MCI_CONTINUE_TRANSFER)
|| (pCommand->blockSize == 0)) {
WRITE_MCI(pMciHw, MCI_ARGR, pCommand->arg);
WRITE_MCI(pMciHw, MCI_CMDR, pCommand->cmd);
}
// In case of transmit, the PDC shall be enabled after sending the command
if ((pCommand->blockSize > 0) && !(pCommand->isRead)) {
WRITE_MCI(pMciHw, MCI_PTCR, AT91C_PDC_TXTEN);
}
// Ignore data error
// if (pCommand->blockSize == 0) {
{
mciIer &= ~(AT91C_MCI_UNRE | AT91C_MCI_OVRE \
| AT91C_MCI_DTOE | AT91C_MCI_DCRCE);
}
// Interrupt enable shall be done after PDC TXTEN and RXTEN
WRITE_MCI(pMciHw, MCI_IER, mciIer);
return 0;
}
//------------------------------------------------------------------------------
/// Check NOTBUSY and DTIP bits of status register on the given MCI driver.
/// Return value, 0 for bus ready, 1 for bus busy
/// \param pMci Pointer to a MCI driver instance.
//------------------------------------------------------------------------------
unsigned char MCI_CheckBusy(Mci *pMci)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
unsigned int status;
// Enable MCI clock
MCI_Enable(pMci, ENABLE);
status = READ_MCI(pMciHw, MCI_SR);
// trace_LOG(trace_DEBUG, "status %x\n\r",status);
if(((status & AT91C_MCI_NOTBUSY)!=0)
&& ((status & AT91C_MCI_DTIP)==0)) {
// Disable MCI clock
MCI_Enable(pMci, DISABLE);
return 0;
}
else {
return 1;
}
}
//------------------------------------------------------------------------------
/// Check BLKE bit of status register on the given MCI driver.
/// \param pMci Pointer to a MCI driver instance.
//------------------------------------------------------------------------------
unsigned char MCI_CheckBlke(Mci *pMci)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
unsigned int status;
status = READ_MCI(pMciHw, MCI_SR);
// trace_LOG(trace_DEBUG, "status %x\n\r",status);
if((status & AT91C_MCI_BLKE)!=0) {
return 0;
}
else {
return 1;
}
}
//------------------------------------------------------------------------------
/// Processes pending events on the given MCI driver.
/// \param pMci Pointer to a MCI driver instance.
//------------------------------------------------------------------------------
void MCI_Handler(Mci *pMci)
{
AT91S_MCI *pMciHw = pMci->pMciHw;
MciCmd *pCommand = pMci->pCommand;
unsigned int status;
unsigned char i;
#if defined(at91rm9200)
unsigned int mciCr, mciSdcr, mciMr, mciDtor;
#endif
SANITY_CHECK(pMci);
SANITY_CHECK(pMciHw);
SANITY_CHECK(pCommand);
// Read the status register
status = READ_MCI(pMciHw, MCI_SR) & READ_MCI(pMciHw, MCI_IMR);
// trace_LOG(trace_DEBUG, "status %x\n\r", status);
// Check if an error has occured
if ((status & STATUS_ERRORS) != 0) {
// Check error code
if ((status & STATUS_ERRORS) == AT91C_MCI_RTOE) {
pCommand->status = MCI_STATUS_NORESPONSE;
}
// if the command is SEND_OP_COND the CRC error flag is always present
// (cf : R3 response)
else if (((status & STATUS_ERRORS) != AT91C_MCI_RCRCE)
|| ((pCommand->cmd != SDCARD_APP_OP_COND_CMD)
&& (pCommand->cmd != MMC_SEND_OP_COND_CMD))) {
pCommand->status = MCI_STATUS_ERROR;
}
}
// Check if a transfer has been completed
if (((status & AT91C_MCI_CMDRDY) != 0)
|| ((status & AT91C_MCI_ENDRX) != 0)
|| ((status & AT91C_MCI_RXBUFF) != 0)
|| ((status & AT91C_MCI_ENDTX) != 0)
|| ((status & AT91C_MCI_BLKE) != 0)
|| ((status & AT91C_MCI_RTOE) != 0)) {
if (((status & AT91C_MCI_ENDRX) != 0)
|| ((status & AT91C_MCI_RXBUFF) != 0)
|| ((status & AT91C_MCI_ENDTX) != 0)) {
MCI_Enable(pMci, DISABLE);
}
/// On AT91RM9200-EK, if stop transmission, software reset MCI.
#if defined(at91rm9200)
if ((pCommand->cmd & AT91C_MCI_TRCMD_STOP) != 0) {
mciMr = READ_MCI(pMciHw, MCI_MR);
mciSdcr = READ_MCI(pMciHw, MCI_SDCR);
mciDtor = READ_MCI(pMciHw, MCI_DTOR);
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_SWRST);
// trace_LOG(trace_DEBUG, "reset MCI\n\r");
WRITE_MCI(pMciHw, MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_PWSDIS);
WRITE_MCI(pMciHw, MCI_MR, mciMr);
WRITE_MCI(pMciHw, MCI_SDCR, mciSdcr);
WRITE_MCI(pMciHw, MCI_DTOR, mciDtor);
}
#endif
// If no error occured, the transfer is successful
if (pCommand->status == MCI_STATUS_PENDING) {
pCommand->status = 0;
}
#if 0
if ((status & AT91C_MCI_CMDRDY) != 0)
trace_LOG(trace_DEBUG, ".");
if ((status & AT91C_MCI_ENDRX) != 0)
trace_LOG(trace_DEBUG, "<");
if ((status & AT91C_MCI_ENDTX) != 0)
trace_LOG(trace_DEBUG, "-");
if ((status & AT91C_MCI_BLKE) != 0)
trace_LOG(trace_DEBUG, ">");
trace_LOG(trace_DEBUG, "\n\r");
#endif
// Store the card response in the provided buffer
if (pCommand->pResp) {
for (i=0; i < pCommand->resSize; i++) {
pCommand->pResp[i] = READ_MCI(pMciHw, MCI_RSPR[0]);
}
}
// Disable interrupts
WRITE_MCI(pMciHw, MCI_IDR, READ_MCI(pMciHw, MCI_IMR));
// Release the semaphore
pMci->semaphore++;
// Invoke the callback associated with the current command (if any)
if (pCommand->callback) {
(pCommand->callback)(pCommand->status, pCommand);
}
}
}
//------------------------------------------------------------------------------
/// Returns 1 if the given MCI transfer is complete; otherwise returns 0.
/// \param pCommand Pointer to a MciCmd instance.
//------------------------------------------------------------------------------
unsigned char MCI_IsTxComplete(MciCmd *pCommand)
{
if (pCommand->status != MCI_STATUS_PENDING) {
if (pCommand->status != 0)
printf("MCI_IsTxComplete %d\n\r", pCommand->status);
return 1;
}
else {
return 0;
}
}

View File

@ -0,0 +1,159 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support - ROUSSET -
* ----------------------------------------------------------------------------
* Copyright (c) 2006, Atmel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the disclaimer below in the documentation and/or
* other materials provided with the distribution.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 MCI_H
#define MCI_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
/// Transfer is pending.
#define MCI_STATUS_PENDING 1
/// Transfer has been aborted because an error occured.
#define MCI_STATUS_ERROR 2
/// Card did not answer command.
#define MCI_STATUS_NORESPONSE 3
/// MCI driver is currently in use.
#define MCI_ERROR_LOCK 1
/// MCI configuration with 1-bit data bus on slot A (for MMC cards).
#define MCI_MMC_SLOTA 0
/// MCI configuration with 1-bit data bus on slot B (for MMC cards).
#define MCI_MMC_SLOTB 1
/// MCI configuration with 4-bit data bus on slot A (for SD cards).
#define MCI_SD_SLOTA AT91C_MCI_SCDBUS
/// MCI configuration with 4-bit data bus on slot B (for SD cards).
#define MCI_SD_SLOTB (AT91C_MCI_SCDBUS | 1)
/// Start new data transfer
#define MCI_NEW_TRANSFER 0
/// Continue data transfer
#define MCI_CONTINUE_TRANSFER 1
/// MCI SD Bus Width 1-bit
#define MCI_SDCBUS_1BIT (0 << 7)
/// MCI SD Bus Width 4-bit
#define MCI_SDCBUS_4BIT (1 << 7)
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
/// MCI end-of-transfer callback function.
typedef void (*MciCallback)(unsigned char status, void *pCommand);
//------------------------------------------------------------------------------
/// MCI Transfer Request prepared by the application upper layer. This structure
/// is sent to the MCI_SendCommand function to start the transfer. At the end of
/// the transfer, the callback is invoked by the interrupt handler.
//------------------------------------------------------------------------------
typedef struct _MciCmd {
/// Command status.
volatile char status;
/// Command code.
unsigned int cmd;
/// Command argument.
unsigned int arg;
/// Data buffer.
unsigned char *pData;
/// Size of data buffer in bytes.
unsigned short blockSize;
/// Number of blocks to be transfered
unsigned short nbBlock;
/// Indicate if continue to transfer data
unsigned char conTrans;
/// Indicates if the command is a read operation.
unsigned char isRead;
/// Response buffer.
unsigned int *pResp;
/// Size of SD card response in bytes.
unsigned char resSize;
/// Optional user-provided callback function.
MciCallback callback;
/// Optional argument to the callback function.
void *pArg;
} MciCmd;
//------------------------------------------------------------------------------
/// MCI driver structure. Holds the internal state of the MCI driver and
/// prevents parallel access to a MCI peripheral.
//------------------------------------------------------------------------------
typedef struct {
/// Pointer to a MCI peripheral.
AT91S_MCI *pMciHw;
/// MCI peripheral identifier.
unsigned char mciId;
/// Pointer to currently executing command.
MciCmd *pCommand;
/// Mutex.
volatile char semaphore;
} Mci;
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void MCI_Init(
Mci *pMci,
AT91PS_MCI pMciHw,
unsigned char mciId,
unsigned int mode);
extern void MCI_SetSpeed(Mci *pMci, unsigned int mciSpeed);
extern unsigned char MCI_SendCommand(Mci *pMci, MciCmd *pMciCmd);
extern void MCI_Handler(Mci *pMci);
extern unsigned char MCI_IsTxComplete(MciCmd *pMciCmd);
extern unsigned char MCI_CheckBusy(Mci *pMci);
extern void MCI_Close(Mci *pMci);
extern void MCI_SetBusWidth(Mci *pMci, unsigned char busWidth);
#endif //#ifndef MCI_H

View File

@ -0,0 +1,336 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "pio.h"
#include <board.h>
//------------------------------------------------------------------------------
// Internal definitions
//------------------------------------------------------------------------------
/// \internal Returns the current value of a register.
#define READ(peripheral, register) (peripheral->register)
/// \internal Modifies the current value of a register.
#define WRITE(peripheral, register, value) (peripheral->register = value)
//------------------------------------------------------------------------------
// Internal functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Configures one or more pin(s) of a PIO controller as being controlled by
/// peripheral A. Optionally, the corresponding internal pull-up(s) can be
/// enabled.
/// \param pio Pointer to a PIO controller.
/// \param mask Bitmask of one or more pin(s) to configure.
/// \param enablePullUp Indicates if the pin(s) internal pull-up shall be
/// configured.
//------------------------------------------------------------------------------
static void PIO_SetPeripheralA(AT91S_PIO *pio,
unsigned int mask,
unsigned char enablePullUp)
{
// Disable interrupts on the pin(s)
WRITE(pio, PIO_IDR, mask);
// Enable the pull-up(s) if necessary
if (enablePullUp) {
WRITE(pio, PIO_PPUER, mask);
}
else {
WRITE(pio, PIO_PPUDR, mask);
}
// Configure pin
WRITE(pio, PIO_ASR, mask);
WRITE(pio, PIO_PDR, mask);
}
//------------------------------------------------------------------------------
/// Configures one or more pin(s) of a PIO controller as being controlled by
/// peripheral A. Optionally, the corresponding internal pull-up(s) can be
/// enabled.
/// \param pio Pointer to a PIO controller.
/// \param mask Bitmask of one or more pin(s) to configure.
/// \param enablePullUp Indicates if the pin(s) internal pull-up shall be
/// configured.
//------------------------------------------------------------------------------
static void PIO_SetPeripheralB(AT91S_PIO *pio,
unsigned int mask,
unsigned char enablePullUp)
{
// Disable interrupts on the pin(s)
WRITE(pio, PIO_IDR, mask);
// Enable the pull-up(s) if necessary
if (enablePullUp) {
WRITE(pio, PIO_PPUER, mask);
}
else {
WRITE(pio, PIO_PPUDR, mask);
}
// Configure pin
WRITE(pio, PIO_BSR, mask);
WRITE(pio, PIO_PDR, mask);
}
//------------------------------------------------------------------------------
/// Configures one or more pin(s) or a PIO controller as inputs. Optionally,
/// the corresponding internal pull-up(s) and glitch filter(s) can be
/// enabled.
/// \param pio Pointer to a PIO controller.
/// \param mask Bitmask indicating which pin(s) to configure as input(s).
/// \param enablePullUp Indicates if the internal pull-up(s) must be enabled.
/// \param enableFilter Indicates if the glitch filter(s) must be enabled.
//------------------------------------------------------------------------------
static void PIO_SetInput(AT91S_PIO *pio,
unsigned int mask,
unsigned char enablePullUp,
unsigned char enableFilter)
{
// Disable interrupts
WRITE(pio, PIO_IDR, mask);
// Enable pull-up(s) if necessary
if (enablePullUp) {
WRITE(pio, PIO_PPUER, mask);
}
else {
WRITE(pio, PIO_PPUDR, mask);
}
// Enable filter(s) if necessary
if (enableFilter) {
WRITE(pio, PIO_IFER, mask);
}
else {
WRITE(pio, PIO_IFDR, mask);
}
// Configure pin as input
WRITE(pio, PIO_ODR, mask);
WRITE(pio, PIO_PER, mask);
}
//------------------------------------------------------------------------------
/// Configures one or more pin(s) of a PIO controller as outputs, with the
/// given default value. Optionally, the multi-drive feature can be enabled
/// on the pin(s).
/// \param pio Pointer to a PIO controller.
/// \param mask Bitmask indicating which pin(s) to configure.
/// \param defaultValue Default level on the pin(s).
/// \param enableMultiDrive Indicates if the pin(s) shall be configured as
/// open-drain.
/// \param enablePullUp Indicates if the pin shall have its pull-up activated.
//------------------------------------------------------------------------------
static void PIO_SetOutput(AT91S_PIO *pio,
unsigned int mask,
unsigned char defaultValue,
unsigned char enableMultiDrive,
unsigned char enablePullUp)
{
// Disable interrupts
WRITE(pio, PIO_IDR, mask);
// Enable pull-up(s) if necessary
if (enablePullUp) {
WRITE(pio, PIO_PPUER, mask);
}
else {
WRITE(pio, PIO_PPUDR, mask);
}
// Enable multi-drive if necessary
if (enableMultiDrive) {
WRITE(pio, PIO_MDER, mask);
}
else {
WRITE(pio, PIO_MDDR, mask);
}
// Set default value
if (defaultValue) {
WRITE(pio, PIO_SODR, mask);
}
else {
WRITE(pio, PIO_CODR, mask);
}
// Configure pin(s) as output(s)
WRITE(pio, PIO_OER, mask);
WRITE(pio, PIO_PER, mask);
}
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Configures a list of Pin instances, which can either hold a single pin or a
/// group of pins, depending on the mask value; all pins are configured by this
/// function.
/// Returns 1 if the configuration has been performed successfully; otherwise 0.
/// \param list Pointer to a list of Pin instances.
/// \param size Size of the Pin list (see <PIO_LISTSIZE>).
//------------------------------------------------------------------------------
unsigned char PIO_Configure(const Pin *list, unsigned int size)
{
// Configure pins
while (size > 0) {
switch (list->type) {
case PIO_PERIPH_A:
PIO_SetPeripheralA(list->pio,
list->mask,
(list->attribute & PIO_PULLUP) ? 1 : 0);
break;
case PIO_PERIPH_B:
PIO_SetPeripheralB(list->pio,
list->mask,
(list->attribute & PIO_PULLUP) ? 1 : 0);
break;
case PIO_INPUT:
AT91C_BASE_PMC->PMC_PCER = 1 << list->id;
PIO_SetInput(list->pio,
list->mask,
(list->attribute & PIO_PULLUP) ? 1 : 0,
(list->attribute & PIO_DEGLITCH)? 1 : 0);
break;
case PIO_OUTPUT_0:
case PIO_OUTPUT_1:
PIO_SetOutput(list->pio,
list->mask,
(list->type == PIO_OUTPUT_1),
(list->attribute & PIO_OPENDRAIN) ? 1 : 0,
(list->attribute & PIO_PULLUP) ? 1 : 0);
break;
default: return 0;
}
list++;
size--;
}
return 1;
}
//------------------------------------------------------------------------------
/// Sets a high output level on one or more pin(s) (if configured as output(s)).
/// \param pin Pointer to a Pin instance describing one or more pins.
//------------------------------------------------------------------------------
void PIO_Set(const Pin *pin)
{
WRITE(pin->pio, PIO_SODR, pin->mask);
}
//------------------------------------------------------------------------------
/// Sets a low output level on one or more pin(s) (if configured as output(s)).
/// \param pin Pointer to a Pin instance describing one or more pins.
//------------------------------------------------------------------------------
void PIO_Clear(const Pin *pin)
{
WRITE(pin->pio, PIO_CODR, pin->mask);
}
//------------------------------------------------------------------------------
/// Returns 1 if one or more PIO of the given Pin instance currently have a high
/// level; otherwise returns 0.
/// \param pin Pointer to a Pin instance describing one or more pins.
//------------------------------------------------------------------------------
unsigned char PIO_Get(const Pin *pin)
{
unsigned int reg;
if ((pin->type == PIO_OUTPUT_0) || (pin->type == PIO_OUTPUT_1)) {
reg = READ(pin->pio, PIO_ODSR);
}
else {
reg = READ(pin->pio, PIO_PDSR);
}
if ((reg & pin->mask) == 0) {
return 0;
}
else {
return 1;
}
}
//------------------------------------------------------------------------------
/// Returns 1 if one or more PIO of the given Pin data to be driven on the I/O line
/// level; otherwise returns 0.
/// \param pin Pointer to a Pin instance describing one or more pins.
//------------------------------------------------------------------------------
unsigned char PIO_GetOutputDataStatus(const Pin *pin)
{
if ((READ(pin->pio, PIO_ODSR) & pin->mask) == 0) {
return 0;
}
else {
return 1;
}
}
//------------------------------------------------------------------------------
/// Returns the value of ISR for the PIO controller of the pin.
/// Reading this register acknoledges all the ITs.
/// \param pin Pointer to a Pin instance describing one or more pins.
//------------------------------------------------------------------------------
unsigned int PIO_GetISR(const Pin *pin)
{
return (READ(pin->pio, PIO_ISR));
}

View File

@ -0,0 +1,169 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \dir
/// !Purpose
///
/// Definition of methods and structures for using PIOs in a transparent
/// way. The main purpose is to allow portability between several boards.
///
/// !Usage
///
/// -# To configure and use pins, see pio.h.
/// -# To enable and use interrupt generation on PIO status change, see
/// pio_it.h.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \unit
/// !Purpose
///
/// Simple & portable usage of PIO pins.
///
/// !Usage
///
/// -# Define a constant pin description array such as the following one:
/// \code
/// const Pin at91board_dbgu[] = {
/// {AT91C_BASE_PIOA, (1 << 30), PIO_PERIPH_A, PIO_DEFAULT},
/// {AT91C_BASE_PIOA, (1 << 31), PIO_PERIPH_A, PIO_DEFAULT},
/// };
/// \endcode
/// Alternatively, constants defined in the piodefs.h header file of the
/// board module can be used:
/// \code
/// const Pin at91board_dbgu[] = {PINS_DBGU};
/// const Pin at91board_usart[] = {PIN_USART0_RXD, PIN_USART0_TXD};
/// \endcode
/// It is possible to group multiple pins if they share the same
/// attributes, to save memory. Here is the previous DBGU example
/// rewritten in such a way:
/// \code
/// const Pin at91board_dbgu[] = {
/// {AT91C_BASE_PIOA, 0xC0000000, PIO_PERIPH_A, PIO_DEFAULT}
/// };
/// \endcode
/// -# For pins configured as inputs, the PIO controller must be enabled
/// in the PMC (*enabled by PIO_Configure at the moment*).
/// -# Configure a pin array by calling PIO_Configure, using
/// the PIO_LISTSIZE macro to calculate the array size if needed. Do not
/// forget to check the return value for any error.
/// -# Set and get the value of a pin using the PIO_Set, PIO_Clear and
/// PIO_Get methods.
//------------------------------------------------------------------------------
#ifndef PIO_H
#define PIO_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "Pin types"
/// This page lists the available types for a Pin instance (in its type field).
/// !Types
/// - PIO_PERIPH_A
/// - PIO_PERIPH_B
/// - PIO_INPUT
/// - PIO_OUTPUT_0
/// - PIO_OUTPUT_1
/// The pin is controlled by the associated signal of peripheral A.
#define PIO_PERIPH_A 0
/// The pin is controlled by the associated signal of peripheral B.
#define PIO_PERIPH_B 1
/// The pin is an input.
#define PIO_INPUT 2
/// The pin is an output and has a default level of 0.
#define PIO_OUTPUT_0 3
/// The pin is an output and has a default level of 1.
#define PIO_OUTPUT_1 4
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "Pin attributes"
/// This page lists the valid values for the attribute field of a Pin instance.
/// !Attributes
/// - PIO_DEFAULT
/// - PIO_PULLUP
/// - PIO_DEGLITCH
/// - PIO_OPENDRAIN
/// Default pin configuration (no attribute).
#define PIO_DEFAULT (0 << 0)
/// The internal pin pull-up is active.
#define PIO_PULLUP (1 << 0)
/// The internal glitch filter is active.
#define PIO_DEGLITCH (1 << 1)
/// The pin is open-drain.
#define PIO_OPENDRAIN (1 << 2)
//------------------------------------------------------------------------------
/// Calculates the size of a Pin instances array. The array must be local (i.e.
/// not a pointer), otherwise the computation will not be correct.
#define PIO_LISTSIZE(list) (sizeof(list) / sizeof(Pin))
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Describes the type and attribute of one PIO pin or a group of similar pins.
typedef struct {
/// Bitmask indicating which pin(s) to configure.
unsigned int mask;
/// Pointer to the PIO controller which has the pin(s).
AT91S_PIO *pio;
/// Peripheral ID of the PIO controller which has the pin(s).
unsigned char id;
/// Pin type (see "Pin types").
unsigned char type;
/// Pin attribute (see "Pin attributes").
unsigned char attribute;
} Pin;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
extern unsigned char PIO_Configure(const Pin *list, unsigned int size);
extern void PIO_Set(const Pin *pin );
extern void PIO_Clear(const Pin *pin);
extern unsigned char PIO_Get(const Pin *pin);
extern unsigned int PIO_GetISR(const Pin *pin);
extern unsigned char PIO_GetOutputDataStatus(const Pin *pin);
#endif //#ifndef PIO_H

View File

@ -0,0 +1,387 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
/// Disable traces for this file
#ifndef NOTRACE
#define NOTRACE
#endif
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "pio_it.h"
#include "pio.h"
#include <aic/aic.h>
#include <board.h>
#include <utility/assert.h>
#include <utility/trace.h>
//------------------------------------------------------------------------------
// Local definitions
//------------------------------------------------------------------------------
/// Returns the current value of a register.
#define READ(peripheral, register) (peripheral->register)
/// Modifies the current value of a register.
#define WRITE(peripheral, register, value) (peripheral->register = value)
/// Maximum number of interrupt sources that can be defined.
#define MAX_INTERRUPT_SOURCES 7
//------------------------------------------------------------------------------
// Local types
//------------------------------------------------------------------------------
/// Describes a PIO interrupt source, including the PIO instance triggering the
/// interrupt and the associated interrupt handler.
typedef struct _InterruptSource {
/// Interrupt source pin.
const Pin *pPin;
/// Interrupt handler.
void (*handler)(const Pin *);
} InterruptSource;
//------------------------------------------------------------------------------
// Local variables
//------------------------------------------------------------------------------
/// List of interrupt sources.
static InterruptSource pSources[MAX_INTERRUPT_SOURCES];
/// Number of currently defined interrupt sources.
static unsigned int numSources;
//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Handles all interrupts on the given PIO controller.
/// \param id PIO controller ID.
/// \param pBase PIO controller base address.
//------------------------------------------------------------------------------
void PioInterruptHandler(unsigned int id, AT91S_PIO *pBase)
{
unsigned int status;
unsigned int i;
// Check PIO controller status
status = pBase->PIO_ISR;
status &= pBase->PIO_IMR;
if (status != 0) {
trace_LOG(trace_DEBUG, "-D- PIO interrupt on PIO controller #%d\n\r", id);
// Check all sources
i = 0;
while (status != 0) {
// There cannot be an unconfigured source enabled.
SANITY_CHECK(i < numSources);
// Source if configured on PIOA
if (pSources[i].pPin->id == id) {
// Source has PIOs which have changed
if ((status & pSources[i].pPin->mask) != 0) {
trace_LOG(trace_DEBUG, "-D- Interrupt source #%d triggered\n\r", i);
pSources[i].handler(pSources[i].pPin);
status &= ~(pSources[i].pPin->mask);
}
}
i++;
}
}
}
//------------------------------------------------------------------------------
/// Generic PIO interrupt handler. Single entry point for interrupts coming
/// from any PIO controller (PIO A, B, C ...). Dispatches the interrupt to
/// the user-configured handlers.
//------------------------------------------------------------------------------
void InterruptHandler()
{
#if defined(AT91C_ID_PIOA)
// Treat PIOA interrupts
PioInterruptHandler(AT91C_ID_PIOA, AT91C_BASE_PIOA);
#endif
#if defined(AT91C_ID_PIOB)
// Treat PIOB interrupts
PioInterruptHandler(AT91C_ID_PIOB, AT91C_BASE_PIOB);
#endif
#if defined(AT91C_ID_PIOC)
// Treat PIOC interrupts
PioInterruptHandler(AT91C_ID_PIOC, AT91C_BASE_PIOC);
#endif
#if defined(AT91C_ID_PIOD)
// Treat PIOD interrupts
PioInterruptHandler(AT91C_ID_PIOD, AT91C_BASE_PIOD);
#endif
#if defined(AT91C_ID_PIOE)
// Treat PIOE interrupts
PioInterruptHandler(AT91C_ID_PIOE, AT91C_BASE_PIOE);
#endif
#if defined(AT91C_ID_PIOABCD)
// Treat PIOABCD interrupts
#if !defined(AT91C_ID_PIOA)
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOA);
#endif
#if !defined(AT91C_ID_PIOB)
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOB);
#endif
#if !defined(AT91C_ID_PIOC)
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOC);
#endif
#if !defined(AT91C_ID_PIOD)
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOD);
#endif
#endif
#if defined(AT91C_ID_PIOABCDE)
// Treat PIOABCDE interrupts
#if !defined(AT91C_ID_PIOA)
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOA);
#endif
#if !defined(AT91C_ID_PIOB)
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOB);
#endif
#if !defined(AT91C_ID_PIOC)
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOC);
#endif
#if !defined(AT91C_ID_PIOD)
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOD);
#endif
#if !defined(AT91C_ID_PIOE)
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOE);
#endif
#endif
#if defined(AT91C_ID_PIOCDE)
// Treat PIOCDE interrupts
#if !defined(AT91C_ID_PIOC)
PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOC);
#endif
#if !defined(AT91C_ID_PIOD)
PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOD);
#endif
#if !defined(AT91C_ID_PIOE)
PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOE);
#endif
#endif
}
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Initializes the PIO interrupt management logic.
/// \param priority PIO controller interrupts priority.
//------------------------------------------------------------------------------
void PIO_InitializeInterrupts(unsigned int priority)
{
trace_LOG(trace_DEBUG, "-D- PIO_Initialize()\n\r");
SANITY_CHECK((priority & ~AT91C_AIC_PRIOR) == 0);
// Reset sources
numSources = 0;
#ifdef AT91C_ID_PIOA
// Configure PIO interrupt sources
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOA\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOA;
AT91C_BASE_PIOA->PIO_ISR;
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOA, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOA);
#endif
#ifdef AT91C_ID_PIOB
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOB\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOB;
AT91C_BASE_PIOB->PIO_ISR;
AT91C_BASE_PIOB->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOB, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOB);
#endif
#ifdef AT91C_ID_PIOC
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOC\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOC;
AT91C_BASE_PIOC->PIO_ISR;
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOC, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOC);
#endif
#ifdef AT91C_ID_PIOD
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOD\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOD;
AT91C_BASE_PIOC->PIO_ISR;
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOD, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOD);
#endif
#ifdef AT91C_ID_PIOE
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOE\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOE;
AT91C_BASE_PIOC->PIO_ISR;
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOE, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOE);
#endif
#if defined(AT91C_ID_PIOABCD)
// Treat PIOABCD interrupts
#if !defined(AT91C_ID_PIOA) \
&& !defined(AT91C_ID_PIOB) \
&& !defined(AT91C_ID_PIOC) \
&& !defined(AT91C_ID_PIOD)
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOABCD\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOABCD;
AT91C_BASE_PIOA->PIO_ISR;
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOABCD, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOABCD);
#endif
#endif
#if defined(AT91C_ID_PIOABCDE)
// Treat PIOABCDE interrupts
#if !defined(AT91C_ID_PIOA) \
&& !defined(AT91C_ID_PIOB) \
&& !defined(AT91C_ID_PIOC) \
&& !defined(AT91C_ID_PIOD) \
&& !defined(AT91C_ID_PIOE)
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOABCDE\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOABCDE;
AT91C_BASE_PIOA->PIO_ISR;
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOABCDE, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOABCDE);
#endif
#endif
#if defined(AT91C_ID_PIOCDE)
// Treat PIOCDE interrupts
#if !defined(AT91C_ID_PIOC) \
&& !defined(AT91C_ID_PIOD) \
&& !defined(AT91C_ID_PIOE)
trace_LOG(trace_DEBUG, "-D- PIO_Initialize: Configuring PIOC\n\r");
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOCDE;
AT91C_BASE_PIOC->PIO_ISR;
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PIOCDE, priority, InterruptHandler);
AIC_EnableIT(AT91C_ID_PIOCDE);
#endif
#endif
}
//------------------------------------------------------------------------------
/// Configures an interrupt source.
/// \param pPin Interrupt source.
/// \param handler Desired interrupt handler for the source.
//------------------------------------------------------------------------------
void PIO_ConfigureIt(const Pin *pPin, void (*handler)(const Pin *))
{
InterruptSource *pSource;
trace_LOG(trace_DEBUG, "-D- PIO_ConfigureIt()\n\r");
SANITY_CHECK(pPin);
ASSERT(numSources < MAX_INTERRUPT_SOURCES,
"-F- PIO_ConfigureIt: Increase MAX_INTERRUPT_SOURCES\n\r");
// Define new source
trace_LOG(trace_DEBUG, "-D- PIO_ConfigureIt: Defining new source #%d.\n\r", numSources);
pSource = &(pSources[numSources]);
pSource->pPin = pPin;
pSource->handler = handler;
numSources++;
}
//------------------------------------------------------------------------------
/// Enables the given interrupt source if it has been configured.
/// \param pPin Interrupt source to enable.
//------------------------------------------------------------------------------
void PIO_EnableIt(const Pin *pPin)
{
trace_LOG(trace_DEBUG, "-D- PIO_EnableIt()\n\r");
SANITY_CHECK(pPin);
#ifndef NOASSERT
unsigned int i = 0;
unsigned char found = 0;
while ((i < numSources) && !found) {
if (pSources[i].pPin == pPin) {
found = 1;
}
i++;
}
ASSERT(found, "-F- PIO_EnableIt: Interrupt source has not been configured\n\r");
#endif
pPin->pio->PIO_ISR;
pPin->pio->PIO_IER = pPin->mask;
}
//------------------------------------------------------------------------------
/// Disables a given interrupt source.
/// \param pPin Interrupt source to disable.
//------------------------------------------------------------------------------
void PIO_DisableIt(const Pin *pPin)
{
SANITY_CHECK(pPin);
trace_LOG(trace_DEBUG, "-D- PIO_DisableIt()\n\r");
pPin->pio->PIO_IDR = pPin->mask;
}

View File

@ -0,0 +1,61 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \unit
/// !Purpose
///
/// Configuration and handling of interrupts on PIO status changes.
///
/// !Usage
///
/// -# Configure an status change interrupt on one or more pin(s) with
/// PIO_ConfigureIt.
/// -# Enable & disable interrupts on pins using PIO_EnableIt and
/// PIO_DisableIt.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "pio.h"
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void PIO_InitializeInterrupts(unsigned int priority);
extern void PIO_ConfigureIt(const Pin *pPin, void (*handler)(const Pin *));
extern void PIO_EnableIt(const Pin *pPin);
extern void PIO_DisableIt(const Pin *pPin);

View File

@ -0,0 +1,115 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "pit.h"
#include <board.h>
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Initialize the System timer for a period in <20>second with a system clock
/// freq in MHz
/// \param period Period in <20>second.
/// \param pit_frequency System clock frequency in MHz.
//------------------------------------------------------------------------------
void PIT_Init(unsigned int period,
unsigned int pit_frequency)
{
AT91C_BASE_PITC->PITC_PIMR = period? (period * pit_frequency + 8) >> 4 : 0; // +8 to avoid %10 and /10
AT91C_BASE_PITC->PITC_PIMR |= AT91C_PITC_PITEN;
}
//------------------------------------------------------------------------------
/// Set the PIT Periodic Interval Value
//------------------------------------------------------------------------------
void PIT_SetPIV(unsigned int piv)
{
AT91C_BASE_PITC->PITC_PIMR = piv | (AT91C_BASE_PITC->PITC_PIMR & (AT91C_PITC_PITEN | AT91C_PITC_PITIEN));
}
//------------------------------------------------------------------------------
/// Enable the PIT
//------------------------------------------------------------------------------
void PIT_Enable(void)
{
AT91C_BASE_PITC->PITC_PIMR |= AT91C_PITC_PITEN;
}
//----------------------------------------------------------------------------
/// Enable PIT periodic interrupt
//----------------------------------------------------------------------------
void PIT_EnableIT(void)
{
AT91C_BASE_PITC->PITC_PIMR |= AT91C_PITC_PITIEN;
}
//------------------------------------------------------------------------------
/// Disable PIT periodic interrupt
//------------------------------------------------------------------------------
void PIT_DisableIT(void)
{
AT91C_BASE_PITC->PITC_PIMR &= ~AT91C_PITC_PITIEN;
}
//------------------------------------------------------------------------------
/// Read PIT mode register
//------------------------------------------------------------------------------
unsigned int PIT_GetMode(void)
{
return(AT91C_BASE_PITC->PITC_PIMR);
}
//------------------------------------------------------------------------------
/// Read PIT status register
//------------------------------------------------------------------------------
unsigned int PIT_GetStatus(void)
{
return(AT91C_BASE_PITC->PITC_PISR);
}
//------------------------------------------------------------------------------
/// Read PIT CPIV and PICNT without ressetting the counters
//------------------------------------------------------------------------------
unsigned int PIT_GetPIIR(void)
{
return(AT91C_BASE_PITC->PITC_PIIR);
}
//------------------------------------------------------------------------------
/// Read System timer CPIV and PICNT without ressetting the counters
//------------------------------------------------------------------------------
unsigned int PIT_GetPIVR(void)
{
return(AT91C_BASE_PITC->PITC_PIVR);
}

View File

@ -0,0 +1,74 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \unit
/// !Purpose
///
/// Configuration and handling of PIT.
///
/// !Usage
///
/// -# Initialize System timer for a period in <20>second with
/// PIT_Init
/// -# Set the PIT Periodic Interval Value with PIT_SetPIV
/// -# Enable the PIT with PIT_Enable
/// -# Enable & disable PIT interrupts using PIT_EnableInt and
/// PIT_DisableInt
/// -# Read PIT mode register
/// PIT_GetMode
/// -# Read PIT status register
/// PIT_GetStatus
/// -# Read PIT CPIV and PICNT without ressetting the counters
/// PIT_GetPIIR
/// -# Read System timer CPIV and PICNT without ressetting the counters
/// PIT_GetPIVR
//------------------------------------------------------------------------------
#ifndef PIT_H
#define PIT_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
extern void PIT_Init(unsigned int period, unsigned int pit_frequency);
extern void PIT_SetPIV(unsigned int piv);
extern void PIT_Enable(void);
extern void PIT_EnableIT(void);
extern void PIT_DisableIT(void);
extern unsigned int PIT_GetMode(void);
extern unsigned int PIT_GetStatus(void);
extern unsigned int PIT_GetPIIR(void);
extern unsigned int PIT_GetPIVR(void);
#endif //#ifndef PIT_H

View File

@ -0,0 +1,133 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "pmc.h"
#include <board.h>
#include <utility/assert.h>
#include <utility/trace.h>
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
#if defined(at91sam7l64) || defined(at91sam7l128)
//------------------------------------------------------------------------------
/// Sets the fast wake-up inputs that can get the device out of Wait mode.
/// \param inputs Fast wake-up inputs to enable.
//------------------------------------------------------------------------------
void PMC_SetFastWakeUpInputs(unsigned int inputs)
{
SANITY_CHECK((inputs & ~0xFF) == 0);
AT91C_BASE_PMC->PMC_FSMR = inputs;
}
#if !defined(__ICCARM__)
__attribute__ ((section (".ramfunc"))) // GCC
#endif
//------------------------------------------------------------------------------
/// Disables the main oscillator, making the device enter Wait mode.
//------------------------------------------------------------------------------
void PMC_DisableMainOscillatorForWaitMode(void)
{
AT91C_BASE_PMC->PMC_MOR = 0x37 << 16;
while ((AT91C_BASE_PMC->PMC_MOR & AT91C_PMC_MAINSELS) != AT91C_PMC_MAINSELS);
}
#endif
#if defined(at91sam7l)
//------------------------------------------------------------------------------
/// Disables the main oscillator when NOT running on it.
//------------------------------------------------------------------------------
void PMC_DisableMainOscillator(void)
{
AT91C_BASE_PMC->PMC_MOR = 0x37 << 16;
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MAINSELS) == AT91C_PMC_MAINSELS);
}
#endif
//------------------------------------------------------------------------------
/// Disables the processor clock, making the device enter Idle mode.
//------------------------------------------------------------------------------
void PMC_DisableProcessorClock(void)
{
AT91C_BASE_PMC->PMC_SCDR = AT91C_PMC_PCK;
while ((AT91C_BASE_PMC->PMC_SCSR & AT91C_PMC_PCK) != AT91C_PMC_PCK);
}
//------------------------------------------------------------------------------
/// Enables the clock of a peripheral. The peripheral ID (AT91C_ID_xxx) is used
/// to identify which peripheral is targetted.
/// Note that the ID must NOT be shifted (i.e. 1 << AT91C_ID_xxx).
/// \param id Peripheral ID (AT91C_ID_xxx).
//------------------------------------------------------------------------------
void PMC_EnablePeripheral(unsigned int id)
{
SANITY_CHECK(id < 32);
if ((AT91C_BASE_PMC->PMC_PCSR & (1 << id)) == (1 << id)) {
trace_LOG(trace_INFO,
"-I- PMC_EnablePeripheral: clock of peripheral"
" %u is already enabled\n\r",
id);
}
else {
AT91C_BASE_PMC->PMC_PCER = 1 << id;
}
}
//------------------------------------------------------------------------------
/// Disables the clock of a peripheral. The peripheral ID (AT91C_ID_xxx) is used
/// to identify which peripheral is targetted.
/// Note that the ID must NOT be shifted (i.e. 1 << AT91C_ID_xxx).
/// \param id Peripheral ID (AT91C_ID_xxx).
//------------------------------------------------------------------------------
void PMC_DisablePeripheral(unsigned int id)
{
SANITY_CHECK(id < 32);
if ((AT91C_BASE_PMC->PMC_PCSR & (1 << id)) != (1 << id)) {
trace_LOG(trace_INFO,
"-I- PMC_DisablePeripheral: clock of peripheral"
" %u is not enabled\n\r",
id);
}
else {
AT91C_BASE_PMC->PMC_PCDR = 1 << id;
}
}

View File

@ -0,0 +1,53 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 PMC_H
#define PMC_H
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
#if defined(at91sam7l64) || defined(at91sam7l128)
extern void PMC_SetFastWakeUpInputs(unsigned int inputs);
extern void PMC_DisableMainOscillator(void);
extern
#ifdef __ICCARM__
__ramfunc
#endif
void PMC_DisableMainOscillatorForWaitMode(void);
#endif
extern void PMC_DisableProcessorClock(void);
extern void PMC_EnablePeripheral(unsigned int id);
extern void PMC_DisablePeripheral(unsigned int id);
#endif //#ifndef PMC_H

View File

@ -0,0 +1,238 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "pwmc.h"
#include <board.h>
#include <utility/assert.h>
#include <utility/trace.h>
//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Finds a prescaler/divisor couple to generate the desired frequency from
/// MCK.
/// Returns the value to enter in PWMC_MR or 0 if the configuration cannot be
/// met.
/// \param frequency Desired frequency in Hz.
/// \param mck Master clock frequency in Hz.
//------------------------------------------------------------------------------
static unsigned short FindClockConfiguration(
unsigned int frequency,
unsigned int mck)
{
unsigned int divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
unsigned char divisor = 0;
unsigned int prescaler;
SANITY_CHECK(frequency < mck);
// Find prescaler and divisor values
prescaler = (mck / divisors[divisor]) / frequency;
while ((prescaler > 255) && (divisor < 11)) {
divisor++;
prescaler = (mck / divisors[divisor]) / frequency;
}
// Return result
if (divisor < 11) {
trace_LOG(trace_DEBUG, "-D- Found divisor=%u and prescaler=%u for freq=%uHz\n\r",
divisors[divisor], prescaler, frequency);
return prescaler | (divisor << 8);
}
else {
return 0;
}
}
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Configures PWM a channel with the given parameters.
/// The PWM controller must have been clocked in the PMC prior to calling this
/// function.
/// \param channel Channel number.
/// \param prescaler Channel prescaler.
/// \param alignment Channel alignment.
/// \param polarity Channel polarity.
//------------------------------------------------------------------------------
void PWMC_ConfigureChannel(
unsigned char channel,
unsigned int prescaler,
unsigned int alignment,
unsigned int polarity)
{
SANITY_CHECK(prescaler < AT91C_PWMC_CPRE_MCKB);
SANITY_CHECK((alignment & ~AT91C_PWMC_CALG) == 0);
SANITY_CHECK((polarity & ~AT91C_PWMC_CPOL) == 0);
// Disable channel
AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;
// Configure channel
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR = prescaler | alignment | polarity;
}
//------------------------------------------------------------------------------
/// Configures PWM clocks A & B to run at the given frequencies. This function
/// finds the best MCK divisor and prescaler values automatically.
/// \param clka Desired clock A frequency (0 if not used).
/// \param clkb Desired clock B frequency (0 if not used).
/// \param mck Master clock frequency.
//------------------------------------------------------------------------------
void PWMC_ConfigureClocks(unsigned int clka, unsigned int clkb, unsigned int mck)
{
unsigned int mode = 0;
unsigned int result;
// Clock A
if (clka != 0) {
result = FindClockConfiguration(clka, mck);
ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clka);
mode |= result;
}
// Clock B
if (clkb != 0) {
result = FindClockConfiguration(clkb, mck);
ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clkb);
mode |= (result << 16);
}
// Configure clocks
trace_LOG(trace_DEBUG, "-D- Setting PWMC_MR = 0x%08X\n\r", mode);
AT91C_BASE_PWMC->PWMC_MR = mode;
}
//------------------------------------------------------------------------------
/// Sets the period value used by a PWM channel. This function writes directly
/// to the CPRD register if the channel is disabled; otherwise, it uses the
/// update register CUPD.
/// \param channel Channel number.
/// \param period Period value.
//------------------------------------------------------------------------------
void PWMC_SetPeriod(unsigned char channel, unsigned short period)
{
// If channel is disabled, write to CPRD
if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR = period;
}
// Otherwise use update register
else {
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR |= AT91C_PWMC_CPD;
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = period;
}
}
//------------------------------------------------------------------------------
/// Sets the duty cycle used by a PWM channel. This function writes directly to
/// the CDTY register if the channel is disabled; otherwise it uses the
/// update register CUPD.
/// Note that the duty cycle must always be inferior or equal to the channel
/// period.
/// \param channel Channel number.
/// \param duty Duty cycle value.
//------------------------------------------------------------------------------
void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty)
{
SANITY_CHECK(duty <= AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR);
// SAM7S errata
#if defined(at91sam7s16) || defined(at91sam7s161) || defined(at91sam7s32) \
|| defined(at91sam7s321) || defined(at91sam7s64) || defined(at91sam7s128) \
|| defined(at91sam7s256) || defined(at91sam7s512)
ASSERT(duty > 0, "-F- Duty cycle value 0 is not permitted on SAM7S chips.\n\r");
ASSERT((duty > 1) || (AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR & AT91C_PWMC_CALG),
"-F- Duty cycle value 1 is not permitted in left-aligned mode on SAM7S chips.\n\r");
#endif
// If channel is disabled, write to CDTY
if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CDTYR = duty;
}
// Otherwise use update register
else {
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR &= ~AT91C_PWMC_CPD;
AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = duty;
}
}
//------------------------------------------------------------------------------
/// Enables the given PWM channel. This does NOT enable the corresponding pin;
/// this must be done in the user code.
/// \param channel Channel number.
//------------------------------------------------------------------------------
void PWMC_EnableChannel(unsigned char channel)
{
AT91C_BASE_PWMC->PWMC_ENA = 1 << channel;
}
//------------------------------------------------------------------------------
/// Disables the given PWM channel.
/// \param channel Channel number.
//------------------------------------------------------------------------------
void PWMC_DisableChannel(unsigned char channel)
{
AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;
}
//------------------------------------------------------------------------------
/// Enables the period interrupt for the given PWM channel.
/// \param channel Channel number.
//------------------------------------------------------------------------------
void PWMC_EnableChannelIt(unsigned char channel)
{
AT91C_BASE_PWMC->PWMC_IER = 1 << channel;
}
//------------------------------------------------------------------------------
/// Disables the period interrupt for the given PWM channel.
/// \param channel Channel number.
//------------------------------------------------------------------------------
void PWMC_DisableChannelIt(unsigned char channel)
{
AT91C_BASE_PWMC->PWMC_IDR = 1 << channel;
}

View File

@ -0,0 +1,61 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 PWMC_H
#define PWMC_H
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void PWMC_ConfigureChannel(
unsigned char channel,
unsigned int prescaler,
unsigned int alignment,
unsigned int polarity);
extern void PWMC_ConfigureClocks
(unsigned int clka,
unsigned int clkb,
unsigned int mck);
extern void PWMC_SetPeriod(unsigned char channel, unsigned short period);
extern void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty);
extern void PWMC_EnableChannel(unsigned char channel);
extern void PWMC_DisableChannel(unsigned char channel);
extern void PWMC_EnableChannelIt(unsigned char channel);
extern void PWMC_DisableChannelIt(unsigned char channel);
#endif //#ifndef PWMC_H

View File

@ -0,0 +1,188 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
//-----------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------
#include <board.h>
//-----------------------------------------------------------------------------
// Macros
//-----------------------------------------------------------------------------
/// WRITE_RSTC: Write RSTC register
#define WRITE_RSTC(pRstc, regName, value) pRstc->regName = (value)
/// READ_RSTC: Read RSTC registers
#define READ_RSTC(pRstc, regName) (pRstc->regName)
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
/// Keywords to write to the reset registers
#define RSTC_KEY_PASSWORD (0xA5UL << 24)
//-----------------------------------------------------------------------------
// Exported functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// Configure the mode of the RSTC peripheral.
/// The configuration is computed by the lib (AT91C_RSTC_*).
/// \param rstc Pointer to an RSTC peripheral.
/// \param rmr Desired mode configuration.
//-----------------------------------------------------------------------------
void RSTC_ConfigureMode(AT91PS_RSTC rstc, unsigned int rmr)
{
rmr &= ~AT91C_RSTC_KEY;
WRITE_RSTC(rstc, RSTC_RMR, rmr | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Enable/Disable the detection of a low level on the pin NRST as User Reset
/// \param rstc Pointer to an RSTC peripheral.
/// \param enable 1 to enable & 0 to disable.
//-----------------------------------------------------------------------------
void RSTC_SetUserResetEnable(AT91PS_RSTC rstc, unsigned char enable)
{
unsigned int rmr = READ_RSTC(rstc, RSTC_RMR) & (~AT91C_RSTC_KEY);
if (enable) {
rmr |= AT91C_RSTC_URSTEN;
}
else {
rmr &= ~AT91C_RSTC_URSTEN;
}
WRITE_RSTC(rstc, RSTC_RMR, rmr | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Enable/Disable the interrupt of a User Reset (USRTS bit in RSTC_RST).
/// \param rstc Pointer to an RSTC peripheral.
/// \param enable 1 to enable & 0 to disable.
//-----------------------------------------------------------------------------
void RSTC_SetUserResetInterruptEnable(AT91PS_RSTC rstc, unsigned char enable)
{
unsigned int rmr = READ_RSTC(rstc, RSTC_RMR) & (~AT91C_RSTC_KEY);
if (enable) {
rmr |= AT91C_RSTC_URSTIEN;
}
else {
rmr &= ~AT91C_RSTC_URSTIEN;
}
WRITE_RSTC(rstc, RSTC_RMR, rmr | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Setup the external reset length. The length is asserted during a time of
/// pow(2, powl+1) Slow Clock(32KHz). The duration is between 60us and 2s.
/// \param rstc Pointer to an RSTC peripheral.
/// \param powl Power length defined.
//-----------------------------------------------------------------------------
void RSTC_SetExtResetLength(AT91PS_RSTC rstc, unsigned char powl)
{
unsigned int rmr = READ_RSTC(rstc, RSTC_RMR);
rmr &= ~(AT91C_RSTC_KEY | AT91C_RSTC_ERSTL);
rmr |= (powl << 8) & AT91C_RSTC_ERSTL;
WRITE_RSTC(rstc, RSTC_RMR, rmr | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Resets the processor.
/// \param rstc Pointer to an RSTC peripheral.
//-----------------------------------------------------------------------------
void RSTC_ProcessorReset(AT91PS_RSTC rstc)
{
WRITE_RSTC(rstc, RSTC_RCR, AT91C_RSTC_PROCRST | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Resets the peripherals.
/// \param rstc Pointer to an RSTC peripheral.
//-----------------------------------------------------------------------------
void RSTC_PeripheralReset(AT91PS_RSTC rstc)
{
WRITE_RSTC(rstc, RSTC_RCR, AT91C_RSTC_PERRST | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Asserts the NRST pin for external resets.
/// \param rstc Pointer to an RSTC peripheral.
//-----------------------------------------------------------------------------
void RSTC_ExtReset(AT91PS_RSTC rstc)
{
WRITE_RSTC(rstc, RSTC_RCR, AT91C_RSTC_EXTRST | RSTC_KEY_PASSWORD);
}
//-----------------------------------------------------------------------------
/// Return NRST pin level ( 1 or 0 ).
/// \param rstc Pointer to an RSTC peripheral.
//-----------------------------------------------------------------------------
unsigned char RSTC_GetNrstLevel(AT91PS_RSTC rstc)
{
if (READ_RSTC(rstc, RSTC_RSR) & AT91C_RSTC_NRSTL) {
return 1;
}
return 0;
}
//-----------------------------------------------------------------------------
/// Returns 1 if at least one high-to-low transition of NRST (User Reset) has
/// been detected since the last read of RSTC_RSR.
/// \param rstc Pointer to an RSTC peripheral.
//-----------------------------------------------------------------------------
unsigned char RSTC_IsUserReseetDetected(AT91PS_RSTC rstc)
{
if (READ_RSTC(rstc, RSTC_RSR) & AT91C_RSTC_URSTS) {
return 1;
}
return 0;
}
//-----------------------------------------------------------------------------
/// Return 1 if a software reset command is being performed by the reset
/// controller. The reset controller is busy.
/// \param rstc Pointer to an RSTC peripheral.
//-----------------------------------------------------------------------------
unsigned char RSTC_IsBusy(AT91PS_RSTC rstc)
{
if (READ_RSTC(rstc, RSTC_RSR) & AT91C_RSTC_SRCMP) {
return 1;
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More