Jump to content
 

All Activity

This stream auto-updates     

  1. Earlier
  2. I am glad to provide some ideas. Let us know what you find. Learning from others is generally cheaper and less painful than learning from my own mistakes.
  3. I think the C libraries can make linear optimization problems much easier to solve in a system. But they are a general purpose solution intended for the general class of optimization problems. There is a good chance that the code you wrote was far simpler and sufficiently optimal for the task at hand due to task specific optimizations. This story is often the case for embedded systems. A general purpose solution is nice and easy but too big/slow for an embedded microcontroller. As the microcontrollers get larger/faster for low costs then these generic or more complete solutions become available. Interestingly this does not always create a "better" performing solution but it often produces a more reliable, faster to market solution by leveraging more widely developed software that has more hours of operation/debugging on it.
  4. Thanks N9WXU. That helps. I know where to look now.
  5. This is great, thanks for summing that up! So if I wanted to calculate the number of bits that are lost from your example above: Source impedance = 100k; LSb Error = 5LSb; log(5)/log(2) = 2.32 This means that the last 2.32 bits are lost and my ENOB = 9.68?
  6. I used to do a fair amount of optimization using linear programming, it was a bit of a pain to code up so I had hoped this would make that easier ...
  7. This tool does three things. Solve the problem helps you learn to think declaratively Helps you develop constraints. To add code to your project, look at the google OR-Tools. In those tools you will provide constraints and data sets. The tools will then do the solving. I would expect some solutions to take quite a bit of CPU time. Currently I am taking basic modeling in discrete optimization in Coursera to learn more about this topic. Training my mind to describe a problem instead of solving it is actually quite hard.
  8. Provided nothing went wrong in your analysis that bit being set means that somewhere something did a software reset, you just have to figure out where this happened. N9WXU gave some possible causes of the RESET instruction being used, but it could really be anywhere. I have even seen this happen with a bad function pointer jumping to a location which contained const data. As a last resort you can search through the program memory for the RESET instruction (you can do this in MPLAB using the memory views) and set a breakpoint at every location containing the reset instruction. That way you should be able to catch it in the debugger and figure out where the reset is coming from.
  9. This is really cool! Can this tool generate C code that I can use in my applications or does it just solve the problem for me?
  10. Glad you found it helpful JG2018, Let me try and answer your questions. This part is just math and the magic of logarithms. The important identity here is that logx(y) = log10(x)/log10(y), and we know that log2(x) will give us how many bits we need to represent x, but our calculators do not do log2 instead they do log10 , which means we have to take log10(x) / log10(2) to get the number of bits we need to represent the number. The short answer is yes because the leakage contributes to a measurement error, and we measure the ADC error as a proportion of LSB size as we described above. How the leakage contributes is a lot more involved and depends on the construction of the ADC and the methods it uses for sampling. Section 7 above shows the simplified circuit diagram of the ADC input path and you can see where Microchip places the leakage current specified in the datasheet. If you calculate the network currents when this circuit is attached to your circuit under measurement you can determine how much of an effect the leakage will have to your measurements. If the source impedance is small the bulk of the current you feed into the pin will end up going into the sampling capacitor and the leakage will have a small effect, but of course the higher your source impedance is the lower the current into the sampling capacitor and the more of an effect the leakage current will have. We can use the PIC18F47K40 example above since the leakage current is indicated in the simplified diagram in section 7 above. If we have 10k of source impedance and we have an additional 1k of internal impedance on the ADC charge path we have at 3V approx 272uA charging the capacitor. The pin leakage is specified at max to be 125nA, which means that 125nA/272uA is the proportion of the charge current lost to our measurement. In this example it contributes 0.0459% error. For a 10-bit ADC this would mean roughly 0.45 LSB of error (calculated as 0.0459% of the full range which is 2^10). That explains the comment in the device datasheet that if your source impedance is more than 10k this will result in an additional error of more than 0.5 LSB which means it will start contributing to your measurement error. To see how this turns into a larger error we can do the same math with a source impedance of 100k instead. This divides the current by 10 and consequently increases the error to 4.59 LSB of error. This means you can still take the measurements but you will potentially have almost 5 LSB of additional error to contend with. And you cannot calibrate out the leakage as it varies with voltage and temperature and the spec already has it varying from 5nA to 125nA depending on these parameters and also the process variation.
  11. Hello, the information in this post is by far one of the best I've seen for ADCs! Thank you for passing this information along to others! I have a few questions I hope you could answer.. 1. How did you get the equation to calculate the fractional number of bits [Log(# LSb error)/Log(2)]? 2. For a high impedance input, does the voltage offset due to leakage current contribute to the LSb error? Thanks!
  12. I am doing some work with combinatorial optimizers. It is amazing what happens when you turn over one more rock and see what scurries out. There is a whole class of programming called declarative programming and I have worked with Haskel enough to be slightly familiar with the concepts. I just learned about flat zinc and an easier environment called MiniZinc which are completely declarative and can be used to solve optimization problems by describing the constraints a valid solution fits inside. So here is a quick example of a program to find the smallest area rectangle where the area is 10 times the circumference. var 1..1000: side1; var 1..1000: side2; var float: area; var float: circumference; constraint area = side1 * side2; constraint circumference = 2 * side1 + 2 * side2; constraint area = 10*circumference; solve minimize area; output ["side1 = \(side1)\nside2 = \(side2)\narea = \(area)\ncircumference = \(circumference)\n"]; and here is the output showing every iteration. side1 = 420 side2 = 21 area = 8820.0 circumference = 882.0 ---------- side1 = 220 side2 = 22 area = 4840.0 circumference = 484.0 ---------- side1 = 120 side2 = 24 area = 2880.0 circumference = 288.0 ---------- side1 = 100 side2 = 25 area = 2500.0 circumference = 250.0 ---------- side1 = 70 side2 = 28 area = 1960.0 circumference = 196.0 ---------- side1 = 60 side2 = 30 area = 1800.0 circumference = 180.0 ---------- side1 = 45 side2 = 36 area = 1620.0 circumference = 162.0 ---------- side1 = 40 side2 = 40 area = 1600.0 circumference = 160.0 ---------- ========== Finished in 82msec Obviously this is a trivial example but it turns out there is quite a bit of research and libraries in this field. For example the google OR-Tools which could be incorporated in your C code. If you need to optimize something and you can describe what the answer looks like (the constraints) then these tools are pretty good. Of course these problems are NP-Complete, so solutions can take some time. Good Luck.
  13. I have not used harmony or web net server so I have not run into this directly. But there may be a few other places to check that cause resets on other systems. Often the assert() functions will end in a software reset, so your code may not call the reset directly, but if you use assert in your error checks you will reset Some malloc libraries will fail with a reset if there is a heap failure.i.e. the stack runs into the heap. This is often detected with a no-mans land between the stack and the heap. The no-mans land is filled with a magic number. If the magic number changed, the stack ran into the no-mans land and may have corrupted the heap.
  14. I'm seeing some random resets with the PIC32MZ1024EFE100 running the Web Net Server project from Harmony 2.06. The RCON register is 0x00000040 which is the SWR (Software Reset Flag) bit. I'm thinking there is a bug in my code but the SYS_RESET_SoftwareReset() function is not being called. What else would cause the SWR bit to be set? Has anyone experienced a simmilar problem? -Devin
  15. Hey, I just noticed that there are some over-clock options. Here is the result when clocked at 960MHz. I could not get it to run at 1GHz. They did warn that cooling was required.
  16. More Data! I just got a Teensy 4 and it is pretty fast. Compiling it in "fastest" and 600Mhz provides the following results. Strangely compiling it in "faster" provides the slightly better results. (6ns) This is pretty fast but I was expecting a bit more performance since it is 6x faster than the Teensy 3.2 tested before. There is undoubtedly a good reason for this performance, and I expect pin toggling to be limited by wait states in writing to the GPIO peripherals. In any case this is still a fast result.
  17. Looks like it gave the segfault upon running? And yes that is what I would expect because on a PC your code is trying to write to memory which should not permit writes. On an embedded system the behavior will depend a lot on the underlying system. Some systems will actually crash in some way, others, like XC8 based PIC microcontrollers, actually copy the read-only section into RAM so the code will actually work. This is why this is so dangerous, the behavior depends on the target and the toolchain and when this is one day tried on another system it could be a real challenge to figure out the mistake because it is so easily masked.
  18. Thanks for pointing out the mistake, we have updated the text accordingly.
  19. string1[3] = 'a'; This gave a segmentation fault on compilation
  20. It is also permissible to do this: string1[3] = 'a'; Which will change the original string into "striag1". I believe the original string will change to "strang1"
  21. @zakaster - I have posted a more comprehensive answer to the blog here https://www.microforum.cc/blogs/entry/49-a-question-about-structures-strings-and-pointers/
  22. In the comments of our blog on structures in c a member asked me a specific question about what they observed. As this code is a beautiful example of a number of problems we often see we thought it a good idea to make an entry just to discuss this as there really is a lot going on here. We will cover the following: What allocation and freeing of memory on the stack means and the lifetime of objects In which direction the stack usually grows (note - the C standard does not contain the word "stack" so this is compiler-specific) Another look at deep vs. shallow copies of c strings inside structures In order to keep this all generic, I am going to be using the LLVM compiler on my MAC to do all my examples. The examples are all standard C and you can play with the code on your favorite compiler, but since the details of memory allocation are not mandated by the C standard your results may not look exactly like mine. I will e.g. show how the results I get changes when I modify the optimization levels. The Question The OP @zakasterwas asking this: Here is the code snippet they provided: struct person { char* name; }; void get_person(struct person* p) { char new_name[20]; // on stack, gets freed when function returned printf("input new name for person:"); scanf("%s", &new_name); p->name = new_name; printf("address of new_name = %p\n", &new_name[0]); } void eg_test_copy2(void) { struct person p = {"alex"}; get_person(&p); printf("p name = %s\n", p.name); char dummy[20] = { 0 }; printf("address of dummy = %p\n", &dummy[0]); printf("p name = %s\n", p.name); } Variable Allocation When you declare a variable the compiler will only reserve a memory location to be used by the variable. This process will not actually clear the memory unless the variable has static linkage, the standard states that only variables with static linkage (in simple terms this means global variables) shall be initialized to 0. If you want a variable to be initialized you have to supply an initializer. What actually happens before your main function starts running is that something generally referred to as "c-init" will run. This is a bit of code that will do the work needed by the C standard before your code runs, and one of the things it will do is to clear, usually using a loop, the block of memory which will contain statically linked variables. Other things that may be in here are setting up interrupt vectors and other machine registers and of course copying the initial values of global variables that do have initializers over the locations reserved for these variables. When a variable goes "out of scope" the memory is no longer reserved. This simply means that it is free for others to use, it does not mean that the memory is cleared when it is no longer reserved. This is very important to note. This phenomenon often leads to developers testing their code after having a pointer that points to memory which is no longer reserved, and the code seems to work fine until the new owner of that part of memory modifies it, then the code inexplicably breaks! No, it was actually broken all along and you just got lucky that the memory was not used at the time you were accessing this unreserved piece of memory! The classic way this manifests can be seen in our first test (test1) below. #include <stdio.h> char* get_name() { char new_name[20]; // on stack, gets freed when function returned printf("Enter Name:"); scanf("%s", new_name); return new_name; } int main(void) { char* theName; theName = get_name(); printf("\r\nThe name was : %s\r\n", theName); return 0; } I compile and run this and get : > test1 Enter Name:Orunmila The name was : Orunmila Note: Let me mention here that I was using "gcc test1.c -O3" to compile that, when I use the default optimization or -O1 it prints junk instead. When you do something which is undefined in the C standard the behavior will not be guaranteed to be the same on all machines. So I can easily be fooled into thinking this is working just fine, but it is actually very broken! On LLVM I actually get a compiler warning when I compile that as follows: test1.c:7:12: warning: address of stack memory associated with local variable 'new_name' returned [-Wreturn-stack-address] return new_name; ^~~~~~~~ 1 warning generated. Did I mention that I do love LLVM?! We can quickly see how this breaks down if we call the function more than once in a row like this (test2): #include <stdio.h> char* get_name() { char new_name[20]; // on stack, gets freed when function returned printf("Enter Name:"); scanf("%s", new_name); return new_name; } int main(void) { char* theName; char* theSecondName; theName = get_name(); theSecondName = get_name(); printf("\r\nThe first name was : %s\r\n", theName); printf("The second name was : %s\r\n", theSecondName); return 0; } Now we get the following obviously wrong behavior Enter Name:N9WXU Enter Name:Orunmila The first name was : Orunmila The second name was : Orunmila This happens because the declarations of theName and theSecondName in the code only reserve enough memory to store a pointer to a memory location. When the function returns it does not actually return the string containing the name, it only returns the address of the string, the name of the memory location which used to contain the string inside of the function get_name(). At the time when I print the name, the memory is no longer reserved, but as nobody else has used it since I called the function (I did perform any other operation which makes use of the stack in other words). The code is still printing the name, but both name pointers are pointing the same location in memory (which is actually just a coincidence, the compiler would have been within its rights to place the two in different locations). If you call a function that has a local variable between fetching the names and printing them the names will be overwritten by these variables and it will print something which looks like gibberish instead of the names I was typing. We will leave it to the reader to play with this and see how/why this breaks. I would encourage you to also add this to the end, these print statements will clearly show you where the variables are located and why they print the same thing - you will notice that the values of both pointers are the same! printf("Location of theName : %p\r\n", &theName); // This prints the location of the first pointer printf("Location of theSecondName : %p\r\n", &theSecondName); // This prints the location of the second pointer printf("Value of theName : %p\r\n", theName); // This prints the value of the first pointer printf("Value of theSecondName : %p\r\n", theSecondName); // This prints the value of the second pointer This all should answer the question asked, which was "I can still access the old name, is this weird?". The answer is no, this is nor weird at all, but it is undefined and if you called some other functions in between you would see the memory which used to hold the old name being overwritten in weird and wonderful ways as expected. How does the stack grow? Now that we have printed out some pointers this brings us to the next question. Our OP noticed that "the address for `dummy` actually starts after the address of new_name + 4 bytes x 20". We need to be careful here, the C standard requires pointers to be byte-addressable, which means that the address being 20x4 away makes no sense by itself, and in this case it is a pure coincidence. A couple of things should be noted here: The stack usually grows downwards in memory The size of a char[20] buffer will always be 20 and never 4x20 (specified in section 6.5.3.4 of the C99 standard) In the example question the address of new_name was at 0x61FD90, which is actually smaller than 0x61FDE0, and in other words it was placed on the stack AFTER dummy. Here is a diagram which shows a typical layout that a C compiler may choose to use. The reason there was a gap of 80 between the pointers was simply due to the way the compiler decided to place the variables on the stack. It was probably creating some extra space on the stack for passing parameters around and this just happened to be exactly 60 bytes, which resulted in a gap of 80. The C standard only defines the scope of the variables, it does not mandate how the compiler must place them in memory. This can even vary for the same compiler when you add more code as the linker may move things around and will probably change when you change the optimization settings for the compiler. I did some tests with LLVM and if I look at the addresses in the example they will differ significantly when I am using optimization O1, but when I set it to O3 the difference between the two pointers is exactly 20 bytes for the example code. Getting back to Structures and Strings Looking at the intent of the OP's code we can now get back to how structures and strings work in C. With our interface like this struct person { char* name; }; void get_person(struct person* p); What we have is a struct which very importantly does NOT contain a string, it only contains the address of a string. That person struct will reserve (typically) the 4 bytes of RAM required to store a 32-bit address which will be the location where a string exists in memory. If you use it like this you will most often find that the address of "name" will be exactly the same as the address of the person struct you are passing in, so if our OP tested the following this would have been clear: struct person p = {"alex"}; printf("Address of p = %p\n", &p); printf("Address of p.name = %p\n", &p.name); These two addresses must be the same because the struct has only one member! When we want to work with a structure that contains the name of a person we have 2 choices and they both have pro's and con's. Let the struct contain a pointer and use malloc to allocate memory for the string on the heap. (not recommended for embedded projects!) Let the struct contain an array of chars that can contain the name. For option 1 the declaration is fine, but the getName function would have to look as follows: void get_person(struct person* p) { char* new_name = malloc(20); // on heap, so remember to check if it returns NULL ! printf("input new name for person:"); scanf("%s", new_name); p->name = new_name; printf("address of new_name = %p\n", new_name); } Of course, now you have to check and handle the case where we run out of memory and malloc returns NULL, we also have to be cognisant of heap fragmentation and most importantly we now have to be very careful to ensure that the memory gets freed or we will have a memory leak! For option 2 the structure and the function has to change to something like the following: struct person { char name[20]; } void get_person(struct person* p) { printf("input new name for person:"); scanf("%s", p->name); printf("address of p->name = %p\n", p->name); } Of course now we use 20 bytes of memory regardless how long the name is, but on the upside we do not have to worry about freeing the memory, when the instance goes out of scope the compiler will take care of that for us. Also now we can assign one person struct to another which will actually copy the entire string and we still have the option of passing it by reference by using the address of the object! Conclusion Be careful when using C strings in structures, there are a lot of ways these can get you into trouble. Memory leaks and shallow copies, where you make a copy of the pointer but not the string, are very likely to catch you sooner rather than later.
  23. Your example is actually quite complex and it contains a number of really typical and very dangerous mistakes. I think it will take up way too much space here in the comments to answer this properly so I will write another blog just about your question here and post that over the weekend when I get some time to put it together. A hint to what is wrong with the code: The buffer you are using in get_person() is allocated on the stack, but the space is made available for use after the function returns. After this point you have a struct which contains a pointer to the buffer on the stack, but the memory no longer belongs to you and when you call any other function it will get overwritten. There is no rule that the compiler must allocate variables on the stack consecutively, and actually XC8 uses a "compiled stack" which means that variables are placed permanently where they will not overlap. You can probably get behavior closer to what you expect if you change the stack model to use a software stack. The last time this happened to someone in my team they were calling get_person for the first name, and then for the second name, and after calling it twice they tried to print out both names and both the structs had the same string, if you call it many times all the people will have the last name you entered. Try this with your code: struct person p1 = {"nobody"}; struct person p2 = {"nobody"}; get_person(&p1); get_person(&p2); printf("p1 name = %s\n", p1.name); printf("p2 name = %s\n", p2.name); You will enter a new name in get_person for each one, and after that the printing should not print nobody but the 2 different names you have entered, and both names should be different. Let me know if that behaves as you expected? Read my upcoming blog on stack usage and pointers to see why 🙂
  24. sorry i used a duplicated name, for example if i input another name, input new name for person:hello address of new_name = 000000000061FD90 p name = hello address of dummy = 000000000061FDE0 p name = hello
  25. hello sir, really good topic. i have a question regarding the example on deep and shallow copy though, struct person { char* name; }; void get_person(struct person* p) { char new_name[20]; // on stack, gets freed when function returned printf("input new name for person:"); scanf("%s", &new_name); p->name = new_name; printf("address of new_name = %p\n", &new_name[0]); } void eg_test_copy2(void) { struct person p = {"alex"}; get_person(&p); printf("p name = %s\n", p.name); char dummy[20] = { 0 }; printf("address of dummy = %p\n", &dummy[0]); printf("p name = %s\n", p.name); } if i call function `eg_test_copy2`, i noticed that the memory allocated for `new_name` never gets cleared, the output will be input new name for person:alex address of new_name = 000000000061FD90 p name = alex address of dummy = 000000000061FDE0 p name = alex and the address for `dummy` actually starts after the address of new_name + 4 bytes x 20, and i can still access the old name, is this weired
  1. Load more activity
 


  • Popular Contributors

    Nobody has received reputation this week.

  • Who's Online (See full list)

    There are no registered users currently online

  • Forum Statistics

    • Total Topics
      71
    • Total Posts
      337
×
×
  • Create New...