|
Just like I have a secretData_s struct as part of the task data, maybe I should also have a postTaskInfo_s section:
#define PLEASE_SEE_SIZE_OF_POST_TASK_DATA_PARAMETER (1)
struct postTaskInfo_s {
task_t postTask;
taskQueueSelector_e postTaskQueueSelector;
uint16_t sizeOfPostTaskData;
uint8_t postTaskData[PLEASE_SEE_SIZE_OF_POST_TASK_DATA_PARAMETER]
}; Then if postTaskInfo->postTask is not null then the CAN_readRegs-task will queue the postTask just before it's finished. Can someone think of something better?
modified 8-Feb-19 5:58am.
|
|
|
|
|
I am working with embedded programming in standard C and there are 2 microcontrollers communication with each other using a home-made protocol. The scheduler and task queues are also home-made so no realtime operating system is used. My tasks look like this:
void (*task_t)(uint8_t* taskData, uint16_t sizeOfTaskDataInBytes); My add-task-to-queue-function looks like this:
Bool_t addTaskToQueue(queueSelector_e, task_t, uint8_t* taskData, uint16_t sizeOfTaskDataInBytes); Please note that the taskData is also inserted into the queue (in an area called task data pool area) so it's not just a pointer to taskData that is being queued. The task data to write data from microcontroller 1 to microcontroller 2 needs to look something like this:
struct writeRegTaskData_s {
uint32_t startAddr;
uint16_t numRegsToWrite;
uint8_t dataToWrite[numRegsToWrite]; or
uint8_t* dataToWrite } The problem is with the dataToWrite parameter. Sometimes a lot of data will be written, for example, during a firmware upgrade, and then it's overkill to put all the dataToWrite into the queue, it's perfectly fine to have one static buffer. At other times, for example when reacting to different push button events, I want to put my dataToWrite on the stack and then add it into my task queue. However, when I do this, I must make sure dataToWrite points to the proper location inside my task queue data pool and not on the original buffer on the stack. What's the recommended way to solve this issue in a clean way?
|
|
|
|
|
arnold_w wrote: I must make sure dataToWrite points to the proper location inside my task queue data pool and not on the original buffer on the stack. Then you need to allocate a block of memory from the pool, and copy the data there. I assume that you have a pool control mechanism that operates similar to malloc to manage the pool. In either case you pass the buffer pointer to the function that handles the data. You just need to make sure that pool buffers are released after they have been used.
|
|
|
|
|
Yes, the task data pools (one for each task queue) are simple FIFO:s, but they're kept separate from the queues where I store the task function pointers. The only thing I can think of is to place the copied data immediately after the task data struct:
typedef enum {
USE_STATIC_NON_QUEUED_WRITE_DATA = 0,
DATA_TO_WRITE_APPEARS_AFTER_STRUCT = 1
} writeBufferLocationOption_e;
struct writeRegTaskData_s {
uint32_t startAddr;
uint16_t numRegsToWrite;
writeBufferLocationOption_e writeBufferLocationOption;
uint8_t* dataToWrite }
Bool_t addTaskToQueue(queueSelector_e, task_t, uint8_t* taskData1, uint16_t sizeOfTaskDataInBytes1, uint8_t* taskData2, uint16_t sizeOfTaskDataInBytes2); When I do want to queue the data, then I'd pass the data as the taskData2 parameter. Can someone think of a cleaner solution?
|
|
|
|
|
What about something like:
struct writeRegTaskData_s {
writeBufferLocationOption_e writeBufferLocationOption;
uint16_t numRegsToWrite;
uint8_t dataToWrite[1]; }
You can then create your fixed buffer with the above structure, followed immediately by the data space. For the dynamic buffers you just need to calculate the total space required (struct plus data), allocate it and fill in the struct values, and copy the data. You then just have a single pointer to pass to your data maniulation routines.
|
|
|
|
|
I don't think I can guarantee that the static non-queued buffers can be put in the end of the structs because most of the time the structs will be created on the stack. Therefore, I think I need pointers to the buffers.
I also have a CAN-bus interface and typically the receive packet task would take a struct that looks like this:
struct packet_s {
uint32_t extID;
uint8_t DLC; uint8_t* data;
}; In my application these packets will always be queued (they never use static buffers) and as soon as they are queued the data pointer becomes worthless. The best thing I can think of is the following (a slight variation of Richard MacCutchan's suggestion):
#define PLEASE_SEE_DLC_FOR_PACKET_SIZE (1)
struct packet_s {
uint32_t extID;
uint8_t DLC; uint8_t data[PLEASE_SEE_DLC_FOR_PACKET_SIZE];
};
modified 4-Feb-19 9:27am.
|
|
|
|
|
When you declare your static buffers you just add the space required for the struct at the beginning. Then you only need one pointer whether it is static or dynamic. Keep things simple as much as possible.
|
|
|
|
|
Oh, wow, that's a pretty neat trick! Thanks for the suggestion.
|
|
|
|
|
|
|
The following is an example of a simple struct union:
typedef union _UINT32_PART_ {
uint32_t dwWord;
struct {
uint16_t dwMSB;
uint16_t dwLSB;
} hw;
struct {
uint8_t byMSB;
uint8_t byMSBL;
uint8_t byLSBH;
uint8_t byLSB;
} b;
} DWORD_PART;
Is is possible to somehow omit, for example dwMSB and byLSBH, without making the subsequent members point to the wrong things? So, basically this is what I want:
typedef union _UINT32_PART_ {
uint32_t dwWord;
struct {
uint16_t dwLSB; } hw;
struct {
uint8_t byMSB;
uint8_t byMSBL;
uint8_t byLSB; } b;
} DWORD_PART;
|
|
|
|
|
No, and what would be the point?
|
|
|
|
|
To divide a struct into a high-level application level part and a low-level driver part.
|
|
|
|
|
Sorry, but I do not understand what you hope to achieve. A struct is just a description of a block of memory that contains a fixed set of byte s, word s etc. If it is 20 byte s long then you must describe each part exactly so the compiler can calculate the offsets correctly. Leaving blank space and expecting the compiler to guess what is missing makes no sense.
|
|
|
|
|
Yes, but just like you can choose to only initialize parts of a struct:
struct demo_s {
int first;
int second;
int third;
};
struct demo_s demo2 = { .second = 2, .third = 3 }; I was hoping that some union names (hw or b, in the example in my first post) could omit having a reference.
|
|
|
|
|
Whether you initialise all or part of the struct before using it does not matter, except for your code. But you must still define it exactly and completely.
|
|
|
|
|
Yes, but only struct members relevant to the application level should be initialized at the application level. The low-level driver struct members should be initialized at the low-level driver level.
|
|
|
|
|
That is irrelevant; this discussion is about the struct definition. What you do with the content is dependent on your code, but the definition (as I keep repeating) must include every element of the block of memory.
|
|
|
|
|
|
Your first union does the job.
So maybe you give "uint8_t byLSBH;"another name to make it clear it is not relevant?
|
|
|
|
|
I like C but I feel it's Achilles heel is string processing. I've started to do a lot of parsing of text databases in arbitrary format without documentation lately and I need to adapt.
What I need to do is define patterns - expected format for the data and to store the values only if the whole string matches that known pattern. Input validation.
I'd rather not run the rest of my code without verifying the input conforms.
I think regular expressions are the best way to augment my existing skills without learning a new language, but regexes seem kind of varied and mixed breed.
Perl (5?) Seems to have formal standardization of regexes which is supported in many searching and text editing programs. There's also PCRE which I can compile on windows or download precompiled lib/dll.
Should I learn Perl regexes and use PCRE or am I overlooking things?
|
|
|
|
|
There are many websites that help to learn regexes, Expresso Regular Expression Tool[^] is a popular one. But you will also need a support library, as C does not have native support for them.
|
|
|
|
|
As far as I can tell there are at least three main types; POSIX basic, POSIX extended, and Perl Compatible.
There's a list of engines here:
Comparison of regular expression engines - Wikipedia[^]
And apparently some differences between PERL and PCRE:
Perl Compatible Regular Expressions - Wikipedia[^]
I don't know/understand if let's say 80 or 97 percent of the Regex syntax is the same between one version or another or if they are distinct subtypes with significant differences.
I don't know if they all support ascii, Unicode, and utf encoding, or whether they are all capable of returning matched variables or if some are only providing a match/no match result.
|
|
|
|
|
You will have to read the documentation to find out which one suits your requirements best.
|
|
|
|
|
PCRE is a good option. It is based on the Perl regexe, although there are some minor differences under the hood (which I do not remember now). I wrote my own C++ template regex some years ago that gives me full control of behavior in my personal projects. I used other libraries like PCRE, for comparison, in my test bed, to test for speed and accuracy. That is why I know that there are some minor differences on what one considers valid and invalid syntax (implementation differences or programmers mind set - who knows?).
As for Cs ability to process strings or any other data type - it is very efficient. I used to be able to look a C-code and translate it, in my head, directly to the equivalent assembly code. What you are talking about is the standard C libraries, which were designed to provide only the simple low level functionality that programmers require to develop more complex algorithms (how many ways are there to write a 'strcmp' function?). It was left to others to provide libraries that required more than a simple 'for' or while 'loop' in their functions.
That being said, when I find my self doing contract work on old C-code, where I am not allowed to upgrade or use external libraries, I recreate some simple algorithms for parsing (hey its their money, so who am I to argue with a brick wall). Basically, I create equivalent functions for parsing sub-strings like the regex "\d*" or "[abd]" and wrap them in a function call - depending on what I am looking for. What little testing I have done has actually shown me that they were more efficient than using the MS implementation of regex (not a surprise).
Conclusion: C is the most efficient language I have ever work with - there is a reason that all of the modern operating systems, I have worked with, were written in C. (I have not checked lately, so it is possible that C++ snuck in their some were).
INTP
"Program testing can be used to show the presence of bugs, but never to show their absence." - Edsger Dijkstra
"I have never been lost, but I will admit to being confused for several weeks. " - Daniel Boone
|
|
|
|