I have the compile time options of both static and dynamic allocation. Static is in a dedicated section of expansion memory, dynamic is (currently, due to compile time overruns) also in a separate part of memory (user heap defined as 1).
Definition of sizes
// *********************************************************************************************************
// *************************** PCA9634 QUEUE PARAMETERS ****************************************************
// *********************************************************************************************************
#define _DEFAULT_PCA9634_QUEUE_LENGTH 30 // PCA9634 QUEUE
#define _DEFAULT_PCA9634_ITEM_SIZE sizeof (struct I2C_PACKET_data) // STANDARD PACKET SIZE
#define _DEFAULT_PCA9634_STACK_SIZE 14000 // task stack size 7000
This is the definition of the static buffers.
struct PCA9634_static
{
// // BUSY
// StaticSemaphore_t SemaphoreBuffer;
// QUEUE TO TASK
uint8_t QueueStorageArea[(_DEFAULT_PCA9634_QUEUE_LENGTH) * sizeof (union PACKET_DATA)];
StaticQueue_t QueueBuffer;
// TASK STACK
StackType_t stack[_DEFAULT_PCA9634_STACK_SIZE];
// StackType_t* stackptr = (uint8_t*)&stack[0];
StaticTask_t TaskBuffer;
};
struct STATIC_MAP
{
#ifdef _USART
struct USART_static USART_STATIC[_MAX_USART];
#endif
#ifdef _I2C
struct I2C_static I2C_STATIC[_MAX_I2C];
#endif
#ifdef _XHEAP
struct XHEAP_static XHEAP_STATIC;
#endif
#ifdef _PCA9634
struct PCA9634_static PCA9634_STATIC[_MAX_PCA9634];
#endif
#ifdef _SYSTEM_LED
struct LED_static LED_STATIC[_SYSTEM_LED];
#endif
};
This change let me have static allocation mixed with dynamic in terms of code generation, normally the commented lines are in.
//#ifdef _FREERTOS_STATIC
struct STATIC_MAP __attribute__ ((section (".freertos_static_data"))) static_map;
//#endif
This is the create routine. It’s been patched to force the queue to be static, and the task to be dynamic. That’s for debugging, but really, didn’t help.
bool PCA9634::create (uint8_t which, int Interface, int Address)
{
chip_address = Address;
chip_interface = Interface;
if (which < 0) return false;
if (which >= _MAX_PCA9634) return false;
// do not try to initialize hardware on another board
if ((chip_address == 0) || (chip_interface == 0)) return true;
// #ifdef _FREERTOS_STATIC
PCA9634_queue = xQueueCreateStatic(
_DEFAULT_PCA9634_QUEUE_LENGTH, // queue length
_DEFAULT_PCA9634_ITEM_SIZE, // item size
static_map.PCA9634_STATIC[which].QueueStorageArea, // pointer to queue storage byte array
&static_map.PCA9634_STATIC[which].QueueBuffer); // pointer to system queue variable
// #else
// PCA9634_queue = xQueueCreate(_DEFAULT_PCA9634_QUEUE_LENGTH,sizeof(union SYSTEM_PACKET_type));
// #endif
if (PCA9634_queue == nullptr) ERROR1(__FILE__, __LINE__, (char*)"PCA9634 QUEUE CREATE ERROR");
vQueueAddToRegistry(PCA9634_queue, "PCA9634_QUEUE");
#ifdef _FREERTOS_STATIC1
task_handle = xTaskCreateStatic(
(TaskFunction_t) &PCA9634_TASK,
"PCA9634_Task",
_DEFAULT_PCA9634_STACK_SIZE,
(void*) this,
_PCA_TASK_PRIORITY,
static_map.PCA9634_STATIC[(int8_t)which].stack,
&static_map.PCA9634_STATIC[(int8_t)which].TaskBuffer
);
#else
xTaskCreate(
(TaskFunction_t) &PCA9634_TASK,
"PCA9634_Task",
_DEFAULT_PCA9634_STACK_SIZE,
(void*) this,
_PCA_TASK_PRIORITY,
&task_handle);
#endif
if (task_handle == nullptr) ERROR1(__FILE__, __LINE__, (char*)"PCA9634 TASK CREATE ERROR");
return true;
}
Here’s the send routine, which results in an 18 byte packet. I’ll explain that at the end.
uint64_t SYSTEM_PCA9634::COMMAND
(
enum LED_command_type command,
uint8_t which_led,
uint8_t argument[8],
uint8_t delay, // 100 ms intervals
union AT24X_ROM_DATA_type* result_data
)
{
uint64_t error_code = 0;
// code to format for packet based execution
// chip can be local packet controlled or remote packet controlled
uint32_t i;
// set up TX packet header address (format packet does rest of header)
// force local network operations
reply->TX_packet.R.MSG.MSSG.header.address = int (LOCAL_NETWORK) + ((subnet >> 4) & 0xF00) + (node_address & 0xFF);
// at_data_type is an overlay on data_packet.bin
// build generic I2C interface packet, works for most I2C chips
// zero out I2C packet (not chip specific) data
// data is of form command/interface/address/chip/bytecount followed by possible chip specific data bytes
// reply is chip's field
for (i = 0; i < sizeof(reply->TX_packet.R.packet_data.BIN); i++) reply->TX_packet.R.packet_data.BIN[i] = 0;
// build PCA9634 overlay for I2C specific data
// other data will be overlaid on chip specific area
// this is unused if no packets
// I2C generic data
reply->TX_packet.R.packet_data.I2C.interface = chip_interface; // of chip
reply->TX_packet.R.packet_data.I2C.address = chip_address; // of chip
reply->TX_packet.R.packet_data.I2C.chip = I2C_PCA9634; // chip type
reply->TX_packet.R.packet_data.I2C.byte_count = sizeof(union AT24X_ROM_DATA_type); // of chip
// PCA chip specific
reply->TX_packet.R.packet_data.I2C.IC_DATA.PCA.command = command; // duplicated?
reply->TX_packet.R.packet_data.I2C.IC_DATA.PCA.data.which_led = which_led;
for (i = 0; i < 8; i++)
{
reply->TX_packet.R.packet_data.I2C.IC_DATA.PCA.data.value[i] = argument[i];
}
reply->TX_packet.R.packet_data.I2C.IC_DATA.PCA.index = LED_NO_TYPE; // may be wrong
reply->TX_packet.R.packet_data.I2C.IC_DATA.PCA.subsystem = SUBSYSTEM_PCA9634;
reply->TX_packet.R.packet_data.I2C.IC_DATA.PCA.data.delay = delay;
switch (net)
{
// code to format for direct execution
// take program data from calling routines
case NO_NETWORK: // No network (for direct local non-packet control where possible)
{
// this switch must enumerate all implemented chip commands, which propagate directly to the parent class (chip driver)
// no packets are used, no queues are used, reply is not yet set up for response
#ifdef _PERCEPIO_EVENT
xTracePrint(Trace_Channel, "PCA9634 WRITE START");
#endif
// 32 bytes
xQueueSend(PCA9634_queue, &reply->TX_packet.R.packet_data.I2C,portMAX_DELAY );
// xQueueReceive(me->PCA9634_queue, &received_packet, portMAX_DELAY );
and finally, the receive task:
void PCA9634_TASK( void const * argument)
{
PCA9634* me;
union PACKET_DATA received_packet;
struct reply_data_type reply;
enum LED_command_type command;
uint8_t which_led;
uint8_t leds[8];
union AT24X_ROM_DATA_type* result_data;
int i;
uint64_t error_code;
uint16_t delay_time;
me = (PCA9634*) argument;
#ifdef _STATISTICS
statistics_tag(__FILE__, __LINE__, (char*) "Before PCA9634 task");
#endif
while (1)
{
if (uxQueueMessagesWaiting(me->PCA9634_queue) != 0)
{
xQueueReceive(me->PCA9634_queue, &received_packet, 0 );
// message format is meant for I2C
delay_time = 100 * received_packet.I2C.IC_DATA.PCA.data.delay;
result_data = &received_packet.I2C.IC_DATA.AT.rom_data;
command = (enum LED_command_type)received_packet.I2C.IC_DATA.PCA.command;
which_led = received_packet.I2C.IC_DATA.PCA.data.which_led;
for (i = 0; i < 8; i++) leds[i] = received_packet.I2C.IC_DATA.PCA.data.value[i];
result_data->bytes[0] = received_packet.I2C.IC_DATA.PCA.data.value[0];
error_code = me->direct_execute
(
which_led,
command,
&leds[0],
&result_data->bytes[0]);
reply.result = error_code;
vTaskDelay(delay_time);
}
else
{
vTaskDelay(20);
}
}
}
HOW IT WORKS:
The “device”, a PCA9634 LED controller, is created with an assignment of either “NO_NETWORK, LOCAL_NETWORK, NRF_NETWORK…etc.
This assignment controls the transport layer. The device is created at the system driver level, where a command is sent in various ways. NO_NETWORK bypasses the transport layer and talks to the local driver. LOCAL_NETWORK puts the packet on the main queue, where it is removed and routed to the local driver (works wonderfully for debugging), NRF_NETWORK sends the packet over the NRF mesh network.
There’s a separate packet task to read the main queue and send the packet where it needs to go, local or otherwise. This task then puts a fraction of the packet (all that is needed for the chip) on a local queue. The local queue (the queue in question) reads from the PCA9634 queue, executes the command, then delays a designated amount. Without that delay, everything happens at once and you never see what the chip does other than a blink…
The design advantage with the whole system is that from the system standpoint, any device can be remote controlled on any board. All you need is the low level support drivers on the destination board. The user does not need to concern himself with how it happens, the transport layer handles that.
The local (no network) option gives you the fastest response at the cost of disallowing the network options.
The queue behaves identically in either static or dynamic allocation modes.
in list.c, it hangs at this statement.
/* *** NOTE ***********************************************************
If you find your application is crashing here then likely causes are
listed below. In addition see https://www.freertos.org/FAQHelp.html for
more tips, and ensure configASSERT() is defined!
https://www.freertos.org/a00110.html#configASSERT
1) Stack overflow -
see https://www.freertos.org/Stacks-and-stack-overflow-checking.html
2) Incorrect interrupt priority assignment, especially on Cortex-M
parts where numerically high priority values denote low actual
interrupt priorities, which can seem counter intuitive. See
https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition
of configMAX_SYSCALL_INTERRUPT_PRIORITY on
https://www.freertos.org/a00110.html
3) Calling an API function from within a critical section or when
the scheduler is suspended, or calling an API function that does
not end in "FromISR" from an interrupt.
4) Using a queue or semaphore before it has been initialised or
before the scheduler has been started (are interrupts firing
before vTaskStartScheduler() has been called?).
**********************************************************************/
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
}
Stack overflow does not seem to be a problem, and stack overflow hook is enabled.
I’ve worked with interrupt priorities, and I’m not convinced that this is a problem in this case.
There aren’t any critical sections, and these are normal tasks, so no FROM_ISR problems that I can see.
This is done well after the OS is running.
Hope this helps