NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at: www.state-machine.com/video-course and from GitHub: github.com/QuantumLeaps/modern-embedded-programming-course
Yes, presenting high-level concepts from the low-level perspective is the deliberate teaching strategy assumed in this video course. Experience shows that when students see how concepts work at low-level, they tend to later feel more confident in applying the concepts and also they tend to use the concepts more efficiently. --MMS
Thanks Miro. Your lessons in ARM are the best lessons I ever had in my entire embedded career. I learn a lot in your series of lessons. Please keep posting new video for this series. or other related videos in embedded systems. such as embedded design best practices, embedded Linux/ embedded os, etc.. Thanks a lot. Long live :)
Can someone help me to create a clock generator using interrupts.It should be able to generate a clock over a wide range of frequencies.The frequencies should be available in a 1,2,5 step fashion from 1Hz to 2MHz(Table driven!)
Thanks Miro, your videos have been of immense help over the years. Very well structured and explained. For OOP, can you kindly consider the most basic project with a practical example of UML architecture of the project to explain how the thinking process of structuring OOP code in C/C++ works? Thanks
Good OOP design is a deep subject and I'm afraid it would require a whole separate course. In this introduction to OOP I will stick to the simple and very standard "Shapes" example, but I will mention a couple of resources, such as OO design patterns and where to look for more info. --MMS
Thanks for the awesome lesson. Wouldn't it be better to always use &r1.super rather than upcasting (Shape *)&r1 to prevent misalignement and/or allow MI ? If I have SI and MI in my project, it would look inconsistent to sometimes access super classes methods with upcasting or not. What would you consider best practice in this case ?
The best practice is to avoid casting, so use something like &r1.super or &me->super. However, sometimes generic code (e.g., library code) must deal with deeper class hierarchies, where it's unknown whether you need &x1.super or perhaps &x1.super.super. In such a generic code, an explicit upcast is sometimes unavoidable. But in any case, no misalignment should happen as discussed at 7:15. Finally, you mention MI (Multiple Inheritance), which is an entirely different discussion. The simple method presented here obviously works only for Single Inheritance (SI) and does NOT work for MI. And since you ask for best practices, I recommend staying the hell out of MI and don't open that can of worms at all. --MMS
Thanks Miro for the course. But i want a question for this subject. Why doest change x and y variable in rectangle draw function ? Before they were changed in shape move function. I missed this part.
I'm not sure if I understand the question. The Rectangle_draw() operation contains only some commented-out pseud-code to illustrate what it could do, but it certainly does not change x and y attributes. In fact, it cannot do this, because the "me" pointer is declared as 'const * const'. --MMS
Thanks for your videos. The greatest I've encountered. I have a question : why does it break encapsulation to use Shape_moveBy(&r1.super,7, 9) instead of Shape_moveBy((Shape *)&r1, 7, 8). thanks again
In principle you are right. The type cast (like in Shape_moveBy((Shape *)&r1, ...)) is really an "upcast" and as such is safe. But the C compiler does NOT know about it! And safety-related coding standards like MISRA-C generally frown upon type casting, especially pointer casting. For that reason, I wanted to show that casting can be avoided at the expense of exposing the "super" member. --MMS
Not really. Upcasting is treating a subclass like its superclass without paying attention to the semantics of operations. Liskov Substitution Principle (LSP), on the other hand, is all about preserving the *semantics* of operations in the subclasses. For example, the Shape::moveBy() operation should move a Shape by a specified (dx,dy) distance. To comply with LSP, this semantic should apply to all Shape subclasses. So for example, if Rectangle::moveBy() would instead, say, rotate the Rectangle this would violate the LSP. However, it would be just fine for the simple upcasting of the Rectangle subclass to the Shape superclass. --MMS
This question can be understood in at least two ways. If you're asking about one level of inheritance, where you have two Shape subclasses, such as Triangle and Rectangle, then you can upcast Triangle to Shape and Rectangle to Shape. If you are asking about two levels of inheritance, say Shape superclass, inherited by Rectangle, inherited further by ColoredRectangle, you can also upcast ColoredRectangle all the way to Shape. --MMS
@@BinGanzLieb Inheriting two or more superclasses is otherwise known as multiple inheritance (MI). The simple design described in this video is only applicable to single inheritance (SI) and is *not* capable to emulate MI. MI is generally tricky and many experienced developers don't recommend using it. For most purposes, you should use composition instead of MI. --MMS
First and foremost in this day and age it is simply good to know what object-oriented programming is and how it works. One of the best ways to know this is by understanding how OOP works at the low level. This understanding has many benefits. An easier transition from C to C++ is just one of them, but there are others. --MMS
Important Note: void SysTick_Handler(void) -> extern "C" void SysTick_Handler(void) and void GPIOF_Handler(void) to extern "C" void GPIOF_Handler(void) otherwise Keil wont find the handler and will run the weak handlers in the start up code.
You are right that in C++ the exception and interrupt handlers should be defined as extern "C". This is to avoid the C++ "name-mangling". But interestingly, the Keil toolchain does not care, which I'm not sure why. The project builds and works fine with or without extern "C". I'd like to hear an explanation... --MMS
Quantum Leaps, LLC I don’t know actually, but with me it didn’t compile and showed errors, I use no optimisation. Thank you for your efforts, am looking forward for communication protocols course. Your technique of learning by doing helped me greatly.
Can someone help me to create a clock generator using interrupts.It should be able to generate a clock over a wide range of frequencies.The frequencies should be available in a 1,2,5 step fashion from 1Hz to 2MHz(Table driven!)
This sounds like your homework assignment. Well, you need to do it yourself. But if you are interested in interrupts, they are discussed in lessons 16-18. --MMS
NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at:
www.state-machine.com/video-course
and from GitHub:
github.com/QuantumLeaps/modern-embedded-programming-course
Best lessens I have ever encountered on OOP, this is the right way to teach OOP, starting from scratch!
Yes, presenting high-level concepts from the low-level perspective is the deliberate teaching strategy assumed in this video course. Experience shows that when students see how concepts work at low-level, they tend to later feel more confident in applying the concepts and also they tend to use the concepts more efficiently. --MMS
thank you Miro, it was a great pleasure watching this episode of the great embedded systems programming series
Thanks Miro. Your lessons in ARM are the best lessons I ever had in my entire embedded career. I learn a lot in your series of lessons. Please keep posting new video for this series. or other related videos in embedded systems. such as embedded design best practices, embedded Linux/ embedded os, etc.. Thanks a lot. Long live :)
I can't wait to see your next lesson :)
Can someone help me to create a clock generator using interrupts.It should be able to generate a clock over a wide range of frequencies.The frequencies should be available in a 1,2,5 step fashion from 1Hz to 2MHz(Table driven!)
All the content in your channel is fantastic, thank you!!
Very good video!!!
Thanks Miro, your videos have been of immense help over the years. Very well structured and explained. For OOP, can you kindly consider the most basic project with a practical example of UML architecture of the project to explain how the thinking process of structuring OOP code in C/C++ works?
Thanks
Good OOP design is a deep subject and I'm afraid it would require a whole separate course. In this introduction to OOP I will stick to the simple and very standard "Shapes" example, but I will mention a couple of resources, such as OO design patterns and where to look for more info. --MMS
Thanks Miro
Expert as usual, keep posting please
thank you for this valuable lesson.
Thanks for the awesome lesson.
Wouldn't it be better to always use &r1.super rather than upcasting (Shape *)&r1 to prevent misalignement and/or allow MI ?
If I have SI and MI in my project, it would look inconsistent to sometimes access super classes methods with upcasting or not.
What would you consider best practice in this case ?
The best practice is to avoid casting, so use something like &r1.super or &me->super. However, sometimes generic code (e.g., library code) must deal with deeper class hierarchies, where it's unknown whether you need &x1.super or perhaps &x1.super.super. In such a generic code, an explicit upcast is sometimes unavoidable. But in any case, no misalignment should happen as discussed at 7:15. Finally, you mention MI (Multiple Inheritance), which is an entirely different discussion. The simple method presented here obviously works only for Single Inheritance (SI) and does NOT work for MI. And since you ask for best practices, I recommend staying the hell out of MI and don't open that can of worms at all. --MMS
@@StateMachineCOM Thanks for the great recommandations !
another great video.
Thanks Miro for the course. But i want a question for this subject. Why doest change x and y variable in rectangle draw function ? Before they were changed in shape move function. I missed this part.
I'm not sure if I understand the question. The Rectangle_draw() operation contains only some commented-out pseud-code to illustrate what it could do, but it certainly does not change x and y attributes. In fact, it cannot do this, because the "me" pointer is declared as 'const * const'. --MMS
@@StateMachineCOM ok, I understand now. Thanks for your reply.
Thanks for your videos. The greatest I've encountered. I have a question : why does it break encapsulation to use Shape_moveBy(&r1.super,7, 9) instead of Shape_moveBy((Shape *)&r1, 7, 8). thanks again
In principle you are right. The type cast (like in Shape_moveBy((Shape *)&r1, ...)) is really an "upcast" and as such is safe. But the C compiler does NOT know about it! And safety-related coding standards like MISRA-C generally frown upon type casting, especially pointer casting. For that reason, I wanted to show that casting can be avoided at the expense of exposing the "super" member. --MMS
Isnt the upcasting related to liskov?
Not really. Upcasting is treating a subclass like its superclass without paying attention to the semantics of operations. Liskov Substitution Principle (LSP), on the other hand, is all about preserving the *semantics* of operations in the subclasses. For example, the Shape::moveBy() operation should move a Shape by a specified (dx,dy) distance. To comply with LSP, this semantic should apply to all Shape subclasses. So for example, if Rectangle::moveBy() would instead, say, rotate the Rectangle this would violate the LSP. However, it would be just fine for the simple upcasting of the Rectangle subclass to the Shape superclass. --MMS
you are the best!
I have another question. what if I inherit 2 or more classes (structs) in C and can I still upcast?
This question can be understood in at least two ways. If you're asking about one level of inheritance, where you have two Shape subclasses, such as Triangle and Rectangle, then you can upcast Triangle to Shape and Rectangle to Shape. If you are asking about two levels of inheritance, say Shape superclass, inherited by Rectangle, inherited further by ColoredRectangle, you can also upcast ColoredRectangle all the way to Shape. --MMS
@@StateMachineCOM I meant what if rectangle has 2 or more superclasses?
@@BinGanzLieb Inheriting two or more superclasses is otherwise known as multiple inheritance (MI). The simple design described in this video is only applicable to single inheritance (SI) and is *not* capable to emulate MI. MI is generally tricky and many experienced developers don't recommend using it. For most purposes, you should use composition instead of MI. --MMS
why it is important to know that to transfer/switch between c and c++, i meant in the real application case.
First and foremost in this day and age it is simply good to know what object-oriented programming is and how it works. One of the best ways to know this is by understanding how OOP works at the low level. This understanding has many benefits. An easier transition from C to C++ is just one of them, but there are others. --MMS
Important Note:
void SysTick_Handler(void) -> extern "C" void SysTick_Handler(void)
and
void GPIOF_Handler(void) to extern "C" void GPIOF_Handler(void)
otherwise Keil wont find the handler and will run the weak handlers in the start up code.
You are right that in C++ the exception and interrupt handlers should be defined as extern "C". This is to avoid the C++ "name-mangling". But interestingly, the Keil toolchain does not care, which I'm not sure why. The project builds and works fine with or without extern "C". I'd like to hear an explanation... --MMS
Quantum Leaps, LLC I don’t know actually, but with me it didn’t compile and showed errors, I use no optimisation.
Thank you for your efforts, am looking forward for communication protocols course. Your technique of learning by doing helped me greatly.
that was a great video. I also create simillar content.
Excellent tutorial as always!
Luckily we are dealing with classes not Trumps...XD
Can someone help me to create a clock generator using interrupts.It should be able to generate a clock over a wide range of frequencies.The frequencies should be available in a 1,2,5 step fashion from 1Hz to 2MHz(Table driven!)
This sounds like your homework assignment. Well, you need to do it yourself. But if you are interested in interrupts, they are discussed in lessons 16-18. --MMS