Jump to content
 

All Activity

This stream auto-updates     

  1. Earlier
  2. Sometimes I get the sad impression that embedded FW engineers only understand 1 data container, the array. The array is a powerful thing and there are many good reasons to use it but it also has some serious problems. For instance, a number of TCP/IP libraries use arrays to hold a static list of sockets. When you wish to create a new socket the library takes one of the unused sockets from the array and returns a reference (a pointer or index) so the socket can be used as a parameter for the rest of the API. It turns out that sockets are somewhat heavy things (use lots of memory) so you always want to optimize the system to have the smallest number of sockets necessary. Unfortunately, you must "pick" a reasonable number of sockets early in the development process. If you run out of sockets you must go back and recompile the library to utilize the new socket count. Now there is a dependency that is not obvious, only fails at run time and links the feature count of the product with the underlying library. You will see bugs like, "when I am using the app I no longer get notification e-mails". It turns out that this problem can be easily solved with a dynamic container. i.e. one that grows at runtime as you need it to. A brute force method would perhaps be to rely upon the heap to reallocate the array at runtime and simply give the library a pointer to an array. That will work but it inserts a heavy copy operation and the library has to be paused while the old array is migrated to the new array. I propose that you should consider a Linked List. I get a number of concerns from other engineers when I have made this suggestion so just hang tight just one moment. Concerns Allocating the memory requires the heap and my application cannot do that. Traversing the list is complicated and requires recursion. We cannot afford the stack space. A linked list library is a lot of code to solve this problem when a simple array can manage it. The linking pointers use more memory. If you have a new concern, post it below. I will talk about these first. Concern #1, Memory allocation I would argue that a heap is NOT required for a linked list. It is simply the way computer science often teaches the topic. Allocate a block of memory for the data. place the data in the block of memory. Data is often supplied as function parameters. insert the block into the list in the correct place. Computer science courses often teach linked lists and sorting algorithms at the same time so this process forms a powerful association. However, what if the process worked a little differently.j Library Code -> Define a suitable data structure for the user data. Include a pointer for the linked list. User Code -> Create a static instance of the library data structure. Fill it with data. User Code -> Pass a reference to the data structure to the library. Library Code -> insert the data structure into the linked list. If you follow this pattern, the user code can have as many sockets or timers or other widgets as it has memory for. The library will manage the list and operate on the elements. When you delete an element you are simply telling the library to forget but the memory is always owned by the user application. That fixes the data count dependency of the array. Concern #2, Traversing the list is complex and recursive. First, Recursion is always a choice. Just avoid it if that is a rule of your system. Every recursive algorithm can be converted to a loop. .Second, Traversing the list is not much different than an array. The pointer data type is larger so it does take a little longer. struct object_data { int mass; struct object_data *nextObject; }; int findTheMassOfTheObjects(struct object_data *objectList) { thisObject = objectList; while(thisObject) { totalMass += thisObject->mass; thisObject = thisObject->nextObject; } printf("The mass of all the objects is %d grams\n", totalMass); return totalMass; } So here is a quick example. It does have the potential of running across memory if the last object in the list does NOT point at NULL. So that is a potential pitfall. Concern #3, A linked list library is a lot of code Yes it is. Don't do that. A generic library can be done and is a great academic exercise but most of the time the additional next pointers and a few functions to insert and remove objects are sufficient. The "library" should be a collection of code snippets that your developers can copy and paste into the code. This will provide reuse but break the dependency on a common library allowing data types to change, or modifications to be made. Concern #4, A linked list will use more memory It is true that the linked list adds a pointer element to the container data structure. However, this additional memory is probably much smaller than the "just in case" additional memory of unused array elements. It is probably also MUCH better than going back and recompiling an underlying library late in the program and adding a lot more memory so the last bug will not happen again. A little history The linked list was invented by Allen Newell, Cliff Shaw and Herbert Simon. These men were developing IPL (Information Processing Language) and decided that lists were the most suitable solution for containers for IPL. They were eventually awarded a Turing Award for making basic contributions to AI, Psychology of Human Cognition and list processing. Interestingly IPL was developed for a computer called JOHNIAC which had a grand total of 16 kbytes of RAM. Even with only 16KB IPL was very successful and linked lists were determined to be the most suitable design for that problem set. Most of our modern microcontrollers have many times that memory and we are falling back on arrays more and more often. If you are going to insist on an array where a linked list is a better choice, you can rest easy knowing that CACHE memory works MUCH better with arrays simply because you can guarantee that all the data is in close proximity so the entire array is likely living in the cache. Good Luck P.S. - The timeout driver and the TCP library from Microchip both run on 8-bit machines with less than 4KB of RAM and they both use linked lists. Check out the code in MCC for details.
  3. One important rule for good software is Do not Repeat Yourself. This rule is often referred to under the acronym DRY. Most of the time, your code can be made DRY by refactoring repeating blocks of code into a function. This is a good practice but it can lead to a lot of small functions. You undoubtedly will keep your API's tidy by making these functions static and keeping them close to where they are needed but I have recently been working on a lot of C++ code and I have a new tool in my programming toolbox that I would like to share with you. The LAMBDA function. Essentially a lambda is a function that you define inside of another function. This lambda function has function scope so it can only be used in the function that defines it. This can be very useful and will do two things to help keep your code maintainable. It keeps your code readable by forcing you to define a function close to where it is needed. It encourages you to keep the function short & sweet because you will not be tempted to make it a "general purpose solution". Here is an example. I was tasked to implement a serializer where data would be sent on the serial port. This was a binary protocol and it included a special character for start of frame (SOF) and end of frame (EOF). Because the SOF and EOF characters could appear in the actual data, there was an additional data link escape (DLC) character sequence that would expand into the SOF, EOF and DLC. For added fun, there is a checksum that is generated BEFORE the character padding. <SOF><DATA><CHECKSUM><EOF> Here is a function that can decode this message. #define MAXIMUM_MESSAGE_SIZE 255 void dataLink::receiveData(char b) { const char SOF = 0x1A; const char EOF = 0x1B; const char DLC = 0x1C; static enum {findSOF, getData} theState; static int messageIndex=0; static char checksum = 0; static char receivedMessage[MAXIMUM_MESSAGE_SIZE]; switch(theState) { case findSOF: if(b == SOF) { theState = getData; messageIndex = 0; checksum = 0; memset(receivedMessage,0,sizeof(receivedMessage)); } break; case getData: { static bool dlc_last = false; if(dlc_last) { dlc_last = false; switch(b) { case 1: receivedMessage[messageIndex++] = 0x1A; checksum += 0x1A; break; case 2: receivedMessage[messageIndex++] = 0x1B; checksum += 0x1B; break; case 3: receivedMessage[messageIndex++] = 0x1C; checksum += 0x1C; break; } } else { switch(b) { case EOF: theState = findSOF; if(checksum == 0) { //********************* // Do something with the new message //********************* } break; case DLC: dlc_last = true; break; default: receivedMessage[messageIndex++] = b; checksum += b; break; } } break; } } } This function receives a byte and using a few states, creates a checksum validated array of decoded bytes representing the message. I will not explain each line as the details of this function are really not very important. As my code reviewer you should instantly notice that there are 4 sections of nearly identical code that are repeated. In other words, this is not DRY. My first inclination would be to attempt to reorder the decisions so the update of the message array and checksum was done once. This method of course works quite well in this case but I wanted a simple contrived example to show off the lambda function. #define MAXIMUM_MESSAGE_SIZE 255 void dataLink::receiveData(char b) { const char SOF = 0x1A; const char EOF = 0x1B; const char DLC = 0x1C; static enum {findSOF, getData} theState; static int messageIndex=0; static char checksum = 0; static char receivedMessage[MAXIMUM_MESSAGE_SIZE]; auto output_byte = [&](char b) { receivedMessage[messageIndex++] = b; checksum += b; }; switch(theState) { case findSOF: if(b == SOF) { theState = getData; messageIndex = 0; checksum = 0; memset(receivedMessage,0,sizeof(receivedMessage)); } break; case getData: { static bool dlc_last = false; switch(b) { case EOF: theState = findSOF; if(checksum == 0) { //********************* // Do something with the new message //********************* } break; case DLC: dlc_last = true; break; default: if(dlc_last) { dlc_last = false; switch(b) case 1: output_byte(0x1A); break; case 2: output_byte(0x1B); break; case 3: output_byte(0x1C); break; } else { output_byte(b); } break; } break; } } } Now you can see that I moved the work of inserting the byte into the array and updating the checksum into a function called output_byte. This function is defined inside the receiveData function. The syntax has a square bracket followed by parenthesis. The Ampersand inside the brackets indicates that the function has access to all the variables inside receiveData. This makes the function very simple and easy to verify by inspection. Of course you could have made the output_byte function a private member function of the class. But you would have needed to add the checksum and the index variables to the class as well. That increases the complexity of the class. By using the lambda, the function can be made DRY, and readable, and the function does not leak information or variables into any other code. This makes the function much simpler to maintain or potentially refactor in the future. BTW, I tested this function by building the project in the Arduino environment on a SAMD21. The actual Arduino IDE is very limited but when you use VIsual Studio Code and PlatformIO you get full debug on an ATMEL ICE with the Arduino frameworks. This makes developing interesting applications VERY fast. lambda_demo.zip
  4. Some advice for Microchip: If this was my product I would stop selling development kits with A1 or A3 silicon to customers. I2C is widely used and it will create a really bad impression of the product's reliability if customers were to evaluate it with defective silicon. And please fix the Errata, your workaround for I2C Issue 1 does not work as advertized !
  5. Ok some feedback on this one. The workaround in the Errata turns out does not work. The Errata claims you can But this does not work at all. We clear BCL and wait for both S and P bits to be 0 but this never happens and we end up waiting forever. As an attempt to work around this we decided to try to reset the entire module, this means that we set the ON bit in I2CxCON to 0 to disable the module, this resets all the status bits and resets the I2C state machine, once this is done we wait 4 clock cycles (since the second workaround in the Errata suggests we should wait for 4 clock cycles) and then we set the ON bit back to a 1. This clears the BCL error condition correctly and allows us to continue using the peripheral. We have not yet tried to implement the workaround with the timeout that resets the I2C peripheral if it becomes unresponsive without warning, that will be coming up next, but it does seem like that will work fine as it will also disable the entire module when the condition happens which seems to clean out the HW state machine which it looks like is the culprit here. The I2C peripheral section 24 of the family datasheet can be found here http://ww1.microchip.com/downloads/en/devicedoc/61116f.pdf
  6. I am struggling to figure out how to work around what seems to be a silicon bug in the PIC32MZ2048EFM on A1 silicon. I am using the development kit DM320104. From MPLABX I can see that the board I have is running A1 revision silicon. Looking at the Errata for the device I found that there is a silicon Errata on the I2C peripheral and I am hitting at least 2 of the described problems. • False Error Condition 1: False Master Bus Collision Detect (Master-mode only) – The error is indicated through the BCL bit (I2CxSTAT). • False Error Condition 3: Suspended I2C Module Operations (Master or Slave modes) – I2C transactions in progress are inadvertently suspended without error indications. In both cases the Harmony I2C driver ends up in a loop never returning again. For condition 1 the ISR keeps triggering and I2C stops working and for condition 3 the driver just gets stuck. I have tried to implement the workarounds listed in that Errata but I seem to have no luck. The Errata does not have an example, only a text description so I was hoping someone on here has tried this and can help me figure out what I am doing wrong. Currently for condition 1 from the bus collision ISR we are clearing the ISR flag and the BCL bit and then setting the start bit in the I2C1STAT register, but the interrupt keeps on firing away and no start condition is happening. Any idea what we are doing wrong?
  7. Absolutley, and nice examples! Hungarian notation breaks the abstraction of having a variable name with unspecified underlying storage, so I think it is the worst way to leak implementation details!
  8. I think specifically we need to know what processor you are trying to use as this differs from device to device. The simplest and most generic answer would be to add the UART to your project and click on the checkbox to enable interrupts for the driver. After generating code you will have to set the callback which you want called when the interrupt occurs. After this you need to make sure you are enabling interrupts in your main code and it should work. If you supply us with the details above I will post some screenshots for you on how to do this. Just to show you the idea I picked the 16F18875 and added the EUSART as follows: You can see I clicked next to "Enable EUSART Interrupts" Then in my main I ensured the interrupts are enabled. When I now run the code the ISR created by MCC is executed every time a byte is received. The ISR function is called EUSART_Receive_ISR and it is located in the eusart.c file. You can edit this function or replace it by setting a different function as ISR by calling EUSART_SetRxInterruptHandler if you want to change the behavior.
  9. Welcome to our forum, ingbaker. Can you elaborate your question a bit more, please?
  10. How can I do a Interrup with Mcc wendy I received a data?
  11. Yes Doxygen is great for that, it also allows you to click on the boxes and drill down into the details. I use it for this all of the time!
  12. I've got a tip from a colleague: Doxygen also generates dependency and call graphs for a project. So I ran it and after an hour of tweaking and installing things that Doxygen also requires to run I ended up with a nice graph: It doesn't say if the header is actually used by a module, but it certainly is something to get a quick overview of what I'm about to integrate into my project. I always thought of Doxygen being a documentation tool, never thought about the possibility to explore an unknown project with that. Very nice!
  13. With C it can be very tricky. The linker will resolve the symbols at link time and up until then you cannot trace the dependencies in any easy way. You can try with something that does static code analysis, but if you are using #defines it can be unreliable if you do not get all of the settings correct, especially if some of your #ifdefs depend on things the compiler defines for you. The best thing you can do is fully explore which files are being used, even if that means removing them one at a time and testing it all out. Always include only the files that you are reallly using, if you have dead code in your project it just makes it harder to understand and that kind of rot just accumulates over time.
  14. I completely agree with you! I try to avoid comments, because they tend to de-synchronize with the actual code very easily. #pragma config PWRTE = ON // Power-up Timer Enable bit->Power up timer disabled What does that mean? Is it a bugfix? Is it a bug? I find this so often in mature projects. Someone edited bit masks for a register, but forgot to update the comments. Even the Hungarian notation as an extended version of commenting de-synchronizes eventually, because you tend to ignore the prefixes after a while. uint32_t u16counter; So my personal approach is to find identifiers and names to formulate a readable code, which says everything what is actually happening on that specific line of code. There is no need to write functions with many lines of code to make the compiling more efficient or save stack space, modern compilers will optimize that out again. Encapsulating a partial solution not only brings structure to your code, it makes the function actually readable. And with readable I mean to read it out loud in front of an audience (e.g. for a code review). Comments should give the reader a general overview of the problem and an abstract strategy what has to be done in a function. If the purpose is obvious and easily derivable from the identifiers, why create a second meta layer which needs extra maintenance and creates a dependency? This in turn often means that an over-commented code doesn't have a good structure or the author doesn't understand the problem well enough to abstract it. Although sometimes the project dictates code metrics, like a code to comment ratio that has to be satisfied. I see a lot of projects with Doxygen comments in it, but the actual content of that documentation is rather unhelpful.
  15. I struggle to import a certain feature into my microcontroller project. One of my "init" projects needs an eeprom emulation feature and I found a fitting solution. However, the project comes with a dozen ASF files which might be needed, maybe not. To further complicate things, I don't need their hardware initialization. So I could strip their system init function, which leads to more potentially dead files in the project. Is there a more convenient way to get an overview of all the dependencies than clicking through all the includes and declarations manually? I have the feeling I don't need most of the files, but the project is a bit too large to rewrite everything. If I would be very lazy, I'd just throw the whole directory in my project. But I guess this won't end well... So what is your opinion?
  16. Comments I was musing over a piece of code this week trying to figure out why it was doing something that seemed to not make any sense at first glance. The comments in this part of the code were of absolutely no help, they were simply describing what the code was doing. Something like this: // Add 5 to i i += 5; // Send the packet sendPacket(&packet); // Wait on the semaphore sem_wait(&sem); // Increment thread count Threadcount++; These comments just added to the noise in the file, made the code not fit on one page, harder to read and did not tell me anything that the code was not already telling me. What was missing was what I was grappling with. Why was it done this way, why not any other way? I asked a colleague and to my frustration his answer was that he remembered that there was some discussion about this part of the code and that it was done this way for a very good reason! My first response was of course "well why is that not in the comments!?" I remember having conversations about comments being a code smell many times in the past. There is an excellent talk by Kevlin Henney about this on youtube. Just like all other code smells, comments are not universally bad, but whenever I see a comment in a piece of code my spider sense starts tingling and I immediate look a bit deeper to try and understand why comments were actually needed here. Is there not a more elegant way to do this which would not require comments to explain, where reading the code would make what it is doing obvious? WHAT vs. WHY Comments We all agree that good code is code which is properly documented, referring to the right amount of comments, but there is a terrible trap here that programmers seem to fall in all of the time. Instead of documenting WHY they are doing things a particular way, they instead put in the documentation WHAT the code is doing. As Henney explains English, or whatever written language for that matter, is not nearly as precise a language as the programming language used itself. The code is the best way to describe what the code is doing and we hope that someone trying to maintain the code is proficient in the language it is written in, so why all of the WHAT comments? I quite like this Codemanship video, which shows how comments can be a code smell, and how we can use the comments to refactor our code to be more self-explanatory. The key insight here is that if you have to add a comment to a line or a couple of lines of code you can probably refactor the code into a function which has the comment as the name. If you have a line which only calls a function that means that the function is probably not named well enough to be obvious. Consider taking the comment and using it as the name of the function instead. This blog has a number of great examples of how NOT to comment your code, and comical as the examples are the scary part is how often I actually see these kinds of comments in production code! It has a good example of a "WHY" comment as follows. /* don't use the global isFinite() because it returns true for null values */ Number.isFinite(value) So what are we to do, how do we know if comments are good or bad? I would suggest the golden rule must be to test your comment by asking whether is it explaining WHY the code is done this way or if it is stating WHAT the code is doing. If you are stating WHAT the code is doing then consider why you think the comment is necessary in the first place. First, consider deleting the comment altogether, the code is already explaining what is being done after all. Next try to rename things or refactor it into a well-named method or fix the problem in some other way. If the comment is adding context, explaining WHY it was done this way, what else was considered and what the trade-offs were that led to it being done this way, then it is probably a good comment. Quite often we try more than one approach when designing and implementing a piece of code, weighing various metrics/properties of the code to settle finally on the preferred solution. The biggest mistake we make is not to capture any of this in the documentation of the code. This leads to newcomers re-doing all your analysis work, often re-writing the code before realizing something you learned when you wrote it the first time. When you comment your code you should be capturing that kind of context. You should be documenting what was going on in your head when you were writing the code. Nobody should ever read a piece of your code and ask out loud "what were they thinking when they did this?". What you were thinking should be there in plain sight, documented in the comments. Conclusion If you find that you need to find the right person to maintain any piece of code in your system because "he knows what is going on in that code" or even worse "he is the only one that knows" this should be an indication that the documentation is incomplete and more often than not you will find that the comments in this code are explaining WHAT it is doing instead of the WHY's. When you comment your code avoid at all costs explaining WHAT the code is doing. Always test your comments against the golden rule of comments, and if it is explaining what is happening then delete that comment! Only keep the WHY comments and make sure they are complete. And make especially sure that you document the things you considered and concluded would be the wrong thing to do in this piece of code and WHY that is the case.
  17. Because in C89 this would be a syntax error. The syntax did not exist until it was introduced in C99 together with named initializers. In C89 it was not possible to initialize a union by it's second member because it was not possible to name the target member. This is important because many compilers today are still not fully C99 compliant and support only some of it's constructs, which means that if you use named initializers your code may be less portable because some compilers may still choke on that syntax. This example is verbatim from the C99 standard section 6.7.7 paragraph 6. The answer to your question is right there in the last sentence "The first two bit-field declarations differ in that unsigned is a type specifier (which forces t to be the name of a structure member), while const is a type qualifier (which modifies t which is still visible as a typedef name). " So in other words because of the "unsigned" the t is forced to be the name of the member and it is NOT the type of the member as you may expect. This means that when used like that the member is indeed not unnamed, it is named as t of type unsigned and the typedef from above is not applicable at all. I know, that is why even in the standard they refer to this as "obscure"! I have no idea, navigation keys and Enter work just fine for me. I am using Google Chrome, perhaps it is the browser or a setting. Which browser are you using?
  18. Wandering to here, just want to ask some questions : 1. "The type of initialization of a union using the second member of the union was not possible before C99" why? 2. typedef signed int t; typedef int plain; struct tag { unsigned t:4; "one named t that contains values" All I see is a unname member. 3. Why on earth can't I use navigation keys and Enter here???
  19. N9WXU

    How to abstract

    I have seen lots of code that is tightly tied to specific hardware or to specific frameworks. This code is OK because it generally satisfies rule #1 (it must work) but as soon as the HW or framework changes this code becomes very difficult to adapt to the new system. Developers often state that they are abstracted from the hardware by the framework but this is generally never the case because the framework was provided by the hardware vendor. So what is a developer to do? Step #1 Ask the right question. Instead of asking HOW do I do a thing (how do I send bytes over the UART). The developer should ask WHAT do I need to do. Ideally the developer should answer this WHAT question at a pretty high level. WHAT do I need to do? I need to send a packet over RS485. Step #2 Define an API that satisfies the answers to the WHAT questions. If I must send a packet over RS485, then perhaps I need a SendPacket(myPacket) function. In the context of my application this function will be 100% clear to my partner developers. Step #3 Implement a trial of my new API that runs on my PC. This is sufficiently abstract that running my application on my development PC should be trivial. I can access a file, or the network, or the COM ports, or the STDIO and still satisfy the API. Get my partners to kick it around a bit. Repeat #1,#2 & #3 until the API is as clear as possible for THIS application. Step #4 Implement the new API on my HW or framework. This may seem like contributing to Lasagna code.... i.e. just another layer. But in fact this is the true definition of the hardware abstraction layer. ALL details of the HW (or framework) that are not required for THIS application are hidden away and no longer contribute to developer confusion. 100% of what is left is EXACTLY what your application needs. Now you have a chance at producing that mythical self documenting code. You will also find that unit testing the business logic can be more easily accomplished because you will MOCK all functions at this new API layer. Hardware NEVER has to be involved. Good Luck.
  20. I have been working on "STEM" activities with kids of all ages for quite some time. Usually it is with my own kids but often it has been with kids at schools or in the neighborhood. This activity has been very rewarding but there are a few challenges that can quickly make the experience less interesting for the kids and an exercise in frustration for you, the mentor. 1) Don't be spontaneous (but fake it well) - My daughter and I wired a display to a nano and wrote the code to count 0 to 9. This was a perfect bite sized project because I was able to write enough 7-segment abstraction (struct digit { int a:1; int b:1; etc...}; ) to quickly stick a number on the display and I left enough missing code to have her "help" by identifying which segments needed to be active to draw each number. This was a ton of fun and she was suitably engaged. However, on previous occasions we took on too much and the "library" that needed to be thrown together to bring the complexity into reach by a 7 year old was more that I could deliver inside her attention span. So you do need to be prepared for when the kids are motivated to play with electronics... but some of that preparedness might be a stock of ready to go code modules that you can tap into service. 2) Be Prepared with stuff. - I like to keep a pretty well stocked assortment of parts, tools and ingredients for many projects. With prices for components so cheap, I always buy a few extra's for the stock pile to enable a kid with a sudden itch to do something cool. Unfortunately, there are often a few unintended hurdles. For example: I have a large collection of 7-segment displays and a small pile of Arduino Nano's. 3) 3D printers are fun and interesting.... but laser cutters are better and scissors are best. We all like to show off the amazing things you can do with a 3d printer and I have 3 of them. Unfortunately, using a printer requires a few things. a) patience, b) learning to 3d model, c) patience. My kids are quite good at alerting me when my print has turned into a ball of yarn. But none of the kids has developed any interest in 3d modeling for the 3d printer. I also have a fairly large laser cutter. This is FAR more fun and the absolute best tool I have put into my garage. My laster cutter is 130W and cuts about 1.5meters x 1meter or so. We have cut the usual plywood and acrylic. We also cut gingerbread, fabric, paper, and cardboard. (Laser cut gingerbread houses taste bad) I can convert a pencil sketch into a DXF file in a few minutes....BUT the scissors are better for that quick and dirty experiment. which leads to.... 4) Fail Fast and with ZERO effort.... Kids HATE TO WASTE THEIR TIME. Of course what they consider wasted time and what you and I consider wasted time is a different discussion. For example: folding laundry is a waste of time because you will just unfold the laundry to wear the clothes. So it is better to jam everything under the bed. Taking 2 hours to design a 3d model for the laser cutter or 3d printer is a waste of time if the parts don't work when you are done. However, if you can quickly cut something out of cardboard with scissors or a knife, then the time cost is minimal and if it doesn't work out, they are not sad. I have often had a sad kid after an experiment that took a large amount of effort. I thought the experiment worked well and we all learned something but the "wasted effort" was a problem. I have also seen grownups ride a project down in flames because it was "too big to fail" or "we will have wasted all that money if we quit now".. This is the same problem on a grand scale as the kids. So teach them to fail fast and learn from each iteration. As the project develops, the cool tools will be used but not in the first pass. 5) Pick your battles. Guide your charges with small projects that can be "completed" in 30 minutes or so. DO NOT nag them to "finish" if it is not done on the first outing. If the kid finds that project fun, they will hound you to work on it with them. As they develop skills, they will work on parts themselves while you are not around. (watch out for super glue and soldering irons). This is the ideal situation. So you need to do teasers and have fun. They will come back to the fun projects and steer clear of boring ones. So what has worked for me? 1) Rockets. I have bought 12 packs of rockets as classroom kits. I keep a few on stock pile. Once you have a field to fly them you can always get an entire group of kids ready to fly small rockets in an hour or so and they are fun for all ages. 2) Paper Airplanes. Adults and kids can easily burn an afternoon with paper airplanes. Kids by themselves will quickly tire of it so teach them to fold a basic airplane, how to throw and add a little competition. Don't forget to include spectacular crashes as a competition goal because that will keep their spirits up when problems occur. 3) VEX Robotics. I have done FIRST robots, Lego League and VEX robotics. My favorite is VEX IQ because the budget can be reached by a small group of families and the field fits on the back porch. I did have to bribe one daughter who was doing the code with cookies. This started a tradition of "cookies and code". Each task completed earns a cookie. Each bug fixed is a cookie. The rewards are fantastic! 4) Robotics at Home. Robotics are good for kids because they incorporate so many aspects of engineering (Mechanical, Electrical, Software) into one package. You can easily fill in any of these elements while the child explores their interest. One of my daughters likes to build robots. Another likes to program them. I simply remove any technical obstacles, hopefully before they notice them coming. This allows them to keep living in the moment and solving the problems at their level. 5) SCIENCE!. Be prepared to take ANY question they have and turn it into a project. We launched baking soda & vinegar rockets. I did 3d print them so I had to plan ahead. We have also recreated Galileo's gravity experiments in our stairwell. We recorded the difference in the impact of different objects by connecting a microphone to an oscilloscope. We placed the microphone under a piece of wood so the objects would make a sharp noise. We then spent the time trying to release objects at exactly the same time. We used a lever to lift a car!. The child was 5. The lever was a 3 meter steel tube. The car was a small Jeep. We did not lift it very far and we bent the lever but the lesson was learned and will never be forgotten. 6) Change the Oil! Or any other advanced chore. Involve the child in activities that are beyond them but don't leave them stranded. I make sure my new drivers have changed the oil and a tire. I try to involve the younger kids just because they will be underfoot anyway. A child with engineering interests will be make their desires known. In the end you are providing an enriching experience for a child. Keep the experience short & sweet. The objective is to walk away THIS happy. If the experience is positive the child will come back for more. A future lesson can teach ohms law, or software abstraction. The first experiences are just to have fun and do something interesting. Please share your kid project ideas! Include pictures! Good Luck
  21. Problem #14: I2C bus repeaters. A lot of them use miniscule voltage shifts to determine which end is driving the bus so it can drive the signal the right way. If you get them to work reliably, go out and buy some lottery tickets before it's too late.
  22. It has been said that software is the most complicated system humanity has created and like all complicated things we manage this complexity by breaking the problem into small problems. Each small problem is then solved and each solution is assembled to create larger and larger solutions. In other fields of work, humans created standardized solutions to common problems. For example, nails and screws are common solutions to the problem of fastening wood together. Very few carpenters worry about the details of building nails and screws, they simply use them as needed. This practice of creating common solutions to typical problems is also done in software. Software Libraries can easily be used to provide drivers, and advanced functions saving a developer many hours of effort. To make a software library useful, the library developer must create an abstraction of the problem solved by the library. This abstraction must interact with the library user in a simple way and hide the specialist details of the problem. For example, if your task is to convert RGB color values into CMYK color values, you would get a library that had a function such as this one: struct cmyk { float cyan; float magenta; float yellow; float black; }; struct rgb { float red; float green; float blue; }; struct cmyk make_CMYK_from_RGB(struct rgb); This seems very simple and it would absolutely be simple to use. But, if you had to implement such a function yourself you may quickly find your self immersed in color profiles and the behavior of the human eye. All of this complexity is hidden behind a simple function. In the embedded world we often work with hardware and we are very used to silicon vendors providing hardware abstraction layers. These hardware abstraction layers are an attempt to simplify the use use of a vendors hardware and to make it more complicated to switch the hardware to a competing system. Let us go into a little more detail. Here is a typical software layer cake as drawn by a silicon vendor. Often they will provide the bottom 3 layers and even a few demo applications. The hope is you will create your application using their libraries. The benefit for you is a significant time savings (you don't need to make your nails and screws). The benefit to the silicon vendor is getting you locked into a proprietary solution. Here is a short story about the early "dark ages" of computing before PC's had reasonable device drivers (hardware abstraction). In the early days of PC gaming all PC games run in MSDOS. This was to improve game performance be removing any software that was not specifically required. The only sound these early PC had was a simple buzzer so a large number of companies developed a spectacular number of sound cards. There were so many sound cards that PC games could not keep up adding sound card support. Each game had a setup menu where the sound card was selected along with its I/O memory, IRQ, and other esoteric parameters. We had to write the HW configuration down on a cheat sheet and each time we upgraded we had to juggle the physical configuration of our PC (with jumpers) so everything ran without conflict. Eventually, the sound blaster card became the "standard" card and all other vendors either designed their HW to be compatible or wrote their drivers to look just like the sound blaster drivers and achieve compatibility in software. Hardware abstraction has the goal of creating a Hardware interface definition that allows the hardware to present the needed capabilities to the running application. The hardware can have many additional features and capabilities but these are not important to the application so they are not part of the interface. So abstraction provides a simplification by hiding the stuff the application does not care about. The simplification comes from focusing just on the features the application does care about. So if the silicon vendors are providing these abstractions, life can be only good!... We should look a little more closely. Silicon is pretty cheap to make but expensive to design. So each micro controller has a large number of features on each peripheral in the hopes that it will find a home in a large number of applications. Many of these features are mutually exclusive such as synchronous vs asynchronous mode in the EUSART on a PIC micro controller. These features are all well documented in the data sheets but at some point it was decided across the entire industry that if there were functions tied to each feature they would be easier to use. Here is an example from MCC's MSSP driver in SPI mode: void SPI2_ClearWriteCollisionStatus(void) { SSP2CON1bits.WCOL = 0; } Now it may be useful to have a more readable name for the WCOL flag and perhaps ClearWriteCollisionStatus does make the code easier to use. The argument is that making this function call will be more intuitive than clearing the WCOL bit. As you go through many of the HAL layers you find hundreds of examples of very simple functions setting or clearing a few bits. In a few cases you will find an example where all the functions are working together to create a specific abstraction. Most cases, you simply find the HW flags hidden behind more verbosely named functions. Simply renaming the bits is NOT a hardware abstraction. In fact, if the C compiler does not automatically inline these functions they are simply creating overhead. Sadly there is another problem in this mess. The data sheets are very precisely written documents that accurately describe the features of the hardware. Typically these datasheets are written with references to registers and bits. If the vendor provides a comprehensive function interface to the hardware, the data sheet will need to be recreated with function calls and function use examples rather than the bits and registers. In my opinion the term HAL (Hardware Abstraction Layer) has been hijacked to represent a function call interface to all the peripheral functions. What about Board Support Package (BSP)? Generally the BSP is inserted in the layer cake to provide a place for all the code that enables the vendor demo code to run on the "HAL". Arguably, the BSP is what the purist would call the HAL. Enough of the ranting....How does this topic affect you the hapless developer who is likely using vendor code. Silicon Vendors will continue to provide HAL's to interface the hardware, Middleware to provide customers with high function libraries and board support packages to link everything to their proprietary demo boards. As customers, we can evaluate their offering on their systems but we can expect to write our own BSP's to get the rest of their software running on our final product hardware. Software Vendors will continue to provide advanced libraries, RTOS's and other forms of middleware for us to buy to speed our development. The ability to adapt this software to our systems largely depends upon how well the software vendor defines the expected interfaces that we need to provide. Sometimes these vendors can be contracted to get their software running on our hardware and and get us going. FW engineers will continue to spend a significant part of the the project nudging all these pieces into one cohesive system so we can layer our secret sauce on top. One parting comment. Software layers are invented to allow large systems to take advantage of the single responsibility principle. This is great, but if you use too many layers you end up with a new problem called Lasagna code. If you use too few layers you end up with Spaghetti code. One day I would love to know why Italian food is used to name two of the big software smells. Good Luck
  23. This happens from time to time, I have also had periods where nobody could post anything. If that happens please do report it here, it may just help some poor soul who is desperately looking for help 🙂
  24. so maybe it is just slow.................. The webpage at https://www.microchip.com/WebResource.axd?d=s7-zxpMGrfzFqvNoaI5niI88ie3FQikPEzifp9mbqcCCHkN6bVuhHFXDt5AwXEeSdy-rc6bi250kqB7UpkayZtTH4PI1wR37d59huswsxA-21q1hAju4GpYquocl5j0SE3aFtFc2yhhPu09xC3nHWviD5yZKNkkmIzv5gnwfrw7aisbg0&t=636904521560000000 might be temporarily down or it may have moved permanently to a new web address. ERR_NAME_RESOLUTION_FAILED
  25. This morning I find Microchip.com is down 07:15 PDT ( GMT - 8 )
  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
      64
    • Total Posts
      291
×
×
  • Create New...