I recently got hired by a company for a mixed programming position. I had NO experience in embedded systems programming whatsoever and was struggling to catch up. This course single-handedly brought me up to speed with quality and clarity unrivaled by any other UA-cam training course (and I have watched quite a few). If you have just found this course, keep going. You have come to the right place. The fact that Miro Samek has posted this course for free is the height of scholastic generosity. I have learned more from this course than any computer-science college class.
Miro, thank you so much for producing this outstanding embedded programming course. You are so generous to put something so thorough and valuable to those of us wishing to learn embedded programming on UA-cam.
What a great lecture! MACRO and the "volatile" keyword are mentioned briefly in most freshman C programming courses. This lecture reminds me of their importance in firmware programming: define MACRO upon MACRO or fancy MACRO functions, use "volatile" to access MMIO registers or global variables likely modified by interrupts, and go through the headers provided by MCU manufacturers.
This series is put together so incredibly well. It really packs a punch, for someone like me who barely knew the basics of arduino c, this series explains in great depth, and in a way I can actually understand. Thanks to you! Excellent instructor!
Hello, Mr. Miro Samek! I want to thank you very much for making these lessons. It's the best tutorial I found online and it's helping me alot. I am totally excited! I have applied learnt to my STM32F4 board.
Yes, I will devote at least one lesson to the startup code and the "world before C". This will be acutally necessary to explain interrupt handling, because interrupts are defined in the interrupt vector table, which is part of the startup code. Stay tuned... --MMS
These days all software is obtained from the Internet, so don't expect any CD-ROMs anymore. Instead, the box with your board contains a small card, where they list a URL from which you can download the code. But, just as well you can simply google for your board, for example type "TivaC LaunchPad" into Google. On the web-page for the board, TI provides the datasheet, other documentation in PDF, and software. Please be warned that the software, which is called TivaWare, is huge. Alternatively, you can go to the companion web-page advertised in every lesson of this course: www.state-machine.com/quickstart , where you can find all the projects for the lessons. Each such project contains the TM4C123GH6PM.h header file. --MMS
At the end of each lesson I merely ask to subscribe to the StateMachineCOM channel. By doing this you will get a notification when new videos are posted and you will also help to increase the ranking of the channel on UA-cam, which will place the videos higher in the search. In other words, you will help others to find this channel. --MMS
Thank you Doctor Miro for this great resource. I was very confused regarding where to start learning Embedded systems programming but this is just perfect. I'm making progress every day.
Hi Miro, Firstly thank you so much for such a abstract tutorial on embedded systems programming. I have seen a lot of videos online and i have followed some pdf tutorials as well, but none have explained the way you have done. As a beginner in the embedded software world i see you as a first guide in my journey exploring embedded world. You have just cleared blockage in my mind that i had for pointer and dereferencing of the pointers. Miro, you being such a great tutor and expert in embedded systems, i feel you have to take this a step further in explaining complete modules and its interfaces with the ARM, like : SPI, UART, I2C, CAN, TCP/IP (Ethernet), RTOS and may be guide us to develop a real time system using these concepts. Please could you also make a video explaining "function pointers" in embedded systems perspective. Thanks and god bless your family !! (From the bottom of my heart)
3. My plan is to produce a lesson every two weeks or so. I'm actually surprised how time consuming the process of producing each lesson is. The most time goes into editing a lesson down to about 15 minutes.
Thankyouu soo so much. This playlist made me love embedded programming after i hated it for half a semester. You made it easy. So thankyou soo much for your effort❤️
I agree. There are better alternatives to the preprocessor for accessing the hardware registers. Yet, I can't imagine a C programmer unaware of the C preprocessor macros, especially that silicon vendors provide standard header files that still very heavily rely on the preprocessor. My goal for the future lessons is to use the CMSIS (Cortex Microcontroller Software Interface Standard), which maps hardware registers to C structs. But to get there I need to introduce structures. Stay tuned...
Superb!!!! The best online Embedded Programming Course I came across ever!!! Can we expect a full pledged embedded C commercial project some time later on the course. This would greatly help the new embedded programmer seeking job :-) Thanks for your wonderful effort. Awaiting for the great lectures....
Hi Miro, Few queries listed below. Advance thanks for your effort. 1. In the IAR project examples, startup.c file is included, but not found in your code. Can you please explain what is role of startup.c. 2. In another example it is found that two header files are included as #include "inc/hw_memmap.h" #include "inc/hw_timer.h", should we add all those header file into the program or they are built in the package. 3. Eagerly awaiting for the next lectures, when are they coming?
Hello Miro, Your videos are very helpful. Thanks!! I would like to know more about the compiler optimizations and the ways to overcome the unnecessary ones. In addition I would like to know about #pragma directives. Could you add videos on the above topics. Thanks Surya
If you are using the header file tm4c123gh6pm.h: you might get the error: Error[Pe020]: identifier "uint32_t" is undefined If so, you can fix this by also using including this line in your source file: #include (this has the typedef for uint32_t, which is used in tm4c123gh6pm.h instead of unsigned long).
For anyone who is using tm4c123gh6pm, you can download the header file here: users.ece.utexas.edu/~valvano/Volume1/tm4c123gh6pm.h Just right-click then choose Save As and that's it
Hi Mr, Thank you again for this interesting series of videos, this time I want to ask you about what really happned when you pushed the compiler optimization capabilities to "high"; in fact why the while loop controling the counter variable(delay loop) isn't executed????! thnaks fo the explanation best regards @Quantum leaps, LLC
+khaled gharbi The value of the 'counter' variable is not used anywhere inside the loop, except the counter is incremented. The compiler notices this, and treats the whole loop as a very inefficient way of incrementing the 'counter' by the number of iterations. So, instead of executing the loop, the compiler optimizes it out and sets simply counter = 1000000. Please note that the compiler is allowed to optimize your code that way (unless you declare 'counter' as volatile).
+Quantum Leaps, LLC Good morning Mr, it is clear now , as I understand the compiler optimize the whole while loop by seting the counter to 1000000 and as a fact there is no delay. thanks again sir. :)
why at high optimization level the loop is not encountered ? why the last value of counter value is ignored with out using volatile ?please any one can answer ?
You need to look at the code from the compiler's perspective.l A loop like "while (counter < 1000000) { ++counter; }" is to the compiler a horribly inefficient way of changing the "counter" variable from 0 to 1000000. Therefore, the compiler might "optimize" it to "counter += 1000000", or if "counter" is not used afterwards, the compiler can completely eliminate the whole loop. --MMS
2. As you can test for yourself, the project files for the lessons don't include any inc/hw_... files and they work, so you don't need to include them. I will clearly state when any external file is needed and I'll try to explain what it brings. But if you want to understand what these files contain, you should be able already to do so. For example inc/hw_memmap.h contains base addresses of various memory blocks from the LM4F Data Sheet, that I've discussed in Lesson-4.
Love your videos. I'm following this tutorial with a STM32F4 board and you videos really make me understand what is going on inside the MCU. One question however, where can you get Header files, I've been googling for some time now and can't really find anything other then people who made them themselves.
A short explanation is: The data register in fact does not reside in only one address but instead it is mapped to 256 different addresses. One of them is at offset 3FC (another one is 0x000 which I will use as an example to their difference below). According to the datasheet, only bits [9...2] (8 bits) of these addresses are considered and used (or we could say that each address in general is used) to allow reads and writes to a certain permutation of the data register which also has only 8 bits in use (see the datasheet). (for these [9..2] bits in an adress, 1 means i/o to the corresponding bit in the data register is enabled, likewise 0 means i/o is disabled) For example if you write data to address with offset 0x3FC which is: 0000 0000 0000 0000 0000 0011 1111 1100, its bits [9...2] = 1111 1111 => all bits you write to this address will be written to the corresponding bits in the data register too. If, however, you write the data to offset, let's say, 0x000 whose bits [9...2] is 0000 0000 then the data register will NOT be affected no matter what you write into this address. The purpose of this isolation in bits is that GPIO ports usually comprise many pins each and often we would want to perform io on individual pins without affecting others. However, there is only one data register for each GPIO that is shared between pins, e.g. bit 1 of the data register is mapped to pin 1, bit 2 is mapped to pin 2 etc so we need a method like this to be able to perform io exclusively on each of these pins (or a certain group of pins). I think you might want to read the description of the data register in the Register Description section which offers a more thorough explanation. Pay close attention to the part where it describes the memory layout of the data register and how to read from and write to it.
I also got confused about this. I ended up finding the article below: filderbaer.wordpress.com/2014/12/30/2-gpio-programming/ To hopefully help drive the point home, the tutorial instructs us to write 0x0/0x2 to (GPIOF_BASE + 0x3FC) to turn the LED on and off. Another way to turn the LED on and off is to write 0x0/0x2 to (GPIOF_BASE + 0x008).
I have a question about the coloring. I can see that the whole preprocessor directives line in your case is colored blue. On the other hand if I use my IAR Embedded Workbench than only " #define " is colored blue and all after the statement is black and green as rest of the code. Is this an option you can change or is something wrong with my IAR Workbench?
sir as you have said that ti provides header file , but there is nothing i have received other then board and cable in a box , can you tell me how to get that file
Hi Miro, great lesson! I had a question about the compiler optimization. I am running a slightly modified version of the blink code where I alternate between r,g,b for the led after every blink. I noticed that after changing the optimization setting to high and making the relevant variables volatile, the led blinks at a higher rate than when optimization is turned off. When I track the counter variable step by step, I find that it is essentially doing counter+=4 each iteration. How does the compiler decide to do this?
The C compiler is allowed to apply various optimizations to your code, which you apparently experience in this case. The rules of what the compiler is and isn't allowed to do are a bit nuanced, but please search the web for "sequence points in C". The busy-wait delay loops are especially vulnerable to optimizations. To be really stable, such loops shouldn't be based only on the CPU. Instead the delay loops should be based on checking some hardware timer module, which is known to run at a specific frequency. --MMS
Thank you so much for this great course, i'm learning something new in each lesson! I have a question about addressing in assembly by using the PC. Say i have currently stopped the execution on the instruction at address 0x42: stopped here --> 0x42: 0x490d LDR.N R1, [PC, #0x34] (next instruction) 0x44: 0x6008 STR R0, [R1] I know it corresponds to loading R1 with the value 0x400fe608, as correctly happens, but I don't understand how... When i am stopped at the instruction above, I have: PC = 0x00000042 and the addresses which are closer to the "target" one are: 0x72: 0x1c40 ADDS R0, R0, #1 0x74: 0xe7fa B.N 0x6c 0x76: 0xbf00 NOP 0x78: 0x400fe608 DC32 0x400fe608 (1074783752)
The PC-based addressing is made a bit complicated due to the pipeline effects. (Instruction pipeline is touched upon in Lesson 2.). But basically, by the time the PC-based LDR instruction executes, the PC is already incremented to the *next* instruction. In programming practice this is not an issue, because you never calculate the PC offsets by hand. All assemblers provide some help, such as LDR r0,=0x400fe608 will generate a PC-relative load for you.
I had the following warning after adding the macros functions, but the code still works as expected. i just need to be sure that it's not a problem? Warning[Pe1053]: conversion from integer to smaller pointer
tulsidas123 IMHO: When scanning through the code, you are normally more interested in looking for a particular type of variable than whether or not it is volatile or not. Hence, placing the variable type first allows you to scan vertically through your source for instances of "int" instead of "volatile int"
tulsidas123 My recommendation for placing 'volatile' (as well as 'const') after the type comes from the C/C++ guru Dan Saks. Here are a couple of links to his articles that explain the matter (please note that the same discussion applies to both 'volatile' and 'const' qualifiers): www.dansaks.com/articles/1999-02%20const%20T%20vs%20T%20const.pdf www.dansaks.com/articles/1996-12%20Mixing%20const%20with%20Type%20Names.pdf
Thanks for video. I wanted to ask what would be the value at GPIOF_DATA in case that we want to define inputs instead of LEDs that are outpus? Thanks in advance!
I want to learn to use the TM4C123GH6PM.h file given on state-machine but it seems radically different from the one used in this video. How is this new one intended to be used? Edit 2: Watch episode 12 and it will cover it! Edit 1: Hopefully he can correct or confirm this but here's what I've found for those who look at this in the future The "tm4c123gh6pm.h" file makes types for various register maps, and defines the base addresses it needs including the GPIO ports. It casts each base address as the corresponding type, with data members corresponding to things like GPIOF_DIR. To access something like this you need to access the data member in the GPIOF object, so you would say (*GPIOF).DIR to dereference the GPIOF pointer and set the DIR value. The -> operator is a shorthand for dereferencing a pointer to an object and then obtaining a data member of that object, so you could instead put GPIOF -> DIR which is cleaner and preferred. Your code should read " GPIOF -> DIR = 0x0EU; " (without the quotes of course, and actually a value like 0xE works fine too because it's already defined as an unsigned integer and 0xE will translate as 0x0...00E not 0xE00...0). Also, the header file depends on the "core_cm4.h" file which is from the CMSIS library. You can find this specific file on github but it's easier to go to project -> options -> library configuration and enable "use CMSIS." The most confusing part of this to me was "How does the computer know where the address of something like GPIO DIR is, when that value is nowhere in the file?" I suspect that the answer is that it is stealthily encoded into the way the object is made. We take the base address and cast it as a pointer to a GPIO object. This object will hold each data member in memory, starting from the base address of the pointer. The memory locations of these data members like DIR or DATA are at the proper offset because the data members before them take up an amount of memory equal to their offsets. To test this I tried to calculate the address of GPIO DIR knowing only the base address and looking at the code in the header file. We know that a 32 bit unsigned integer takes up 4 bytes of space. The first data member in GPIO is an array of 255 32 bit uints, which means we would expect the next data member, which is DATA, to be 4*255=1020 spaces away, or 3FC in hexadecimal. This matches the offset of DATA. The DATA member is one 4 byte integer, so we would expect the DIR member to be 4 bytes away. 3FC + 4 is 400 in hexadecimal, which matches the offset we had for DIR. It feels a little weird that the memory locations are not explicitly defined, and instead we depend on the way the object's data members are held in memory, but it does work and it's a pretty cool trick. Hopefully this explanation is clear and helps anyone else who is as confused as I was.
I have a question to the memory addresses. I understand the principal of Clock Gating and also the GPIOF_DIR to set the pins as outputs but what exactly is set with the GPIOF_DEN? Also I would like to know if that procedure would apply to other embedded systems? Thanks!
The "GPIOx_DEN" register stands for "Digital Enable". The purpose here is to configure the selected GPIOx pins to be used for digital input/output. This is necessary, because pins coming out of the package are a very limited and precious resource (in fact, pins cost more than the silicon inside). Therefore, pins have multiple functions (e.g., analog, receive/transmit, PWM, etc.), which can be selected in software. Please refer to the TivaC Datasheet, Chapter 10 "GPIO" for more information.
Hi,Sir 1.When would the counter discard? 2.How to sure the bug is caused by optimization or not,especially the normal variable? 3. If I use a timer interrupt func. which has a global variable in it, dose the optimization still would affect the code?
+thentust 1. The compiler is always free to optimize your code, so you cannot assume that a non-volatile counter will be in fact incremented in a slow and very inefficient way (from the compiler's point of view). 2. You simply need to know that the compiler might optimize your code. Therefore, without the 'volatile' keyword your code is incorrect. It just so happens that the code works at lower levels of optimization, but this is an accident. It is not the compiler's problem. It is *your* problem. 3. Global variables inside interrupts are tricky, because they are subject to *race conditions*. I will discuss these issues in the upcoming lesson 20. Stay tuned...
By placing "volatile" or "const" after the type, as opposed to the usual placement before the type, I follow the recommendation of Dan Saks. He explained the reasons in the article "const T vs. T const": www.dansaks.com/articles/1999-02%20const%20T%20vs%20T%20const.pdf . --MMS
Generally, any alternative to the preprocessor (#define) should be preferred in the code. This is because pre-processor macros are expanded *before* the C compiler has a chance to analyze the code, to the compiler cannot perform syntactic analysis on the macros before their expansion. Also, macros can have unintended side effects. One alternative to #define is providing integer constants as enumerations (enum {...}). --MMS
Elico, UA-cam makes it hard to post web links, so please don't expect that you can just click on the link. But, please just take a look at the link in your browser and, if you know anything about URLs, I hope you would see why it doesn't work. I hope it will be obvious to you how to get to the quickstart page. This is the general attitude I would highly recommend in programming--try to think why something doesn't work instead of expecting everything served to you on a plate. --MMS
But how is the compiler exactly messing the variable up? From what is understand the variable is registered to cpu memory and then the compiler places it back to a different ram memory adress or the compiler doesnt place it at all? What is exactly going on?
Actually, the counter variable does not have any memory address before it is declared 'volatile'. The variable is placed in a register. But the really important point here is that the whole delay loop looks to the compiler as a very inefficient way to change 'counter'. Instead of changing 'counter' one count at a time, the compiler is free to optimize it to just one big change. It is like you would deposit $1,000,000 to your bank account by writing million checks for $1. Instead of this, you could simply write one check for $1 million. But then the compiler also notices that you never use the `counter` again, so it optimizes the whole thing away. To prevent all this from happening, you need to declare 'counter' as volatile.
I am using Tiva C series Launchpad and during the Project develop, I also chose 'TI Stellaris' under the debugger/setup/driver, however, when I download the code, it pops the driver warning and therefore the IDE doesn't perform as expected. Does someone in the community also experience such problem and please give me some tips, regarding how to solve it.
Please visit the companion page to this video course at www.state-machine.com/quickstart. Among others, you'll find there the document "Troubleshooting TivaC LaunchPad" in PDF. You can also find there the "Tiva/Stellaris USB Drivers" to update on your machine. --MMS
Hi, I have a question. I know you provided the header file "lm4f120h5qr.h". Where do you download this file from? I ask because I may be using a different board and would like to know where to get the header file for a Different processor.
Hey QP, sorry I'm struggling to understand how 0x0EU sets 1,2 and 3 to output. I'm looking at the data sheet for 0x40025400(GPIO_DIR), on page 660 it says that 0 is input and 1 is output. Not sure how E sets 3 pins to output.
+Helios Fire Remember that bits are numbered from zero, so bit 1 corresponds to 0x02, bit 2 to 0x04 and bit 3 to 0x08. Now, to set all three of these bits, you can create a bitwise OR like this: (0x02 | 0x04 | 0x08) == 0x0E. I hope this helps.
Quantum Leaps, LLC Thanks for the explanation QP, I'm just wondering where I find that information on the datasheet as it only gave me 0 as a input and 1 as an output. If I wanted to set a specific GPIO pin to an output, where would I find it in the datasheet?
+Helios Fire To get a quick overview of the registers for a given peripheral, such as GPIO, you can look for the "Register Map" section. For example, the "GPIO Register Map" starts on page 657. Typically, a "register map" contains only offsets from the base register, because the registers are repeated in every instance of the peripheral, such as GPIOA, GPIOB, ... GPIOF, etc. (The C language offers structures, with which to model the offsets in the code. This concept is discussed in the upcoming lesson 12.) From there, you can see that the GPIO direction is controlled by the GPIODIR register at offset 0x400. The datasheet list the page #, where you can read about this particular register. Again, I hope this helps...
I love your teaching i really understands you perfectly but i want to ask you favor to explain stm32f4-discovery board with keil uvision5 software especially LED and LED matrix
Is there a similar header file of Macros for when I want to program directly in assembly? Or do I need to manually key in the entire register address everytime while programming in assembly? Also, how does the 'volatile' keyword manifest itself in assembly language?
It only manifests itself int the way, that the assembly code will be less optimized. There is not volatile machine instruction, it's something that only the C compiler sees.
dear sir I tried to include the header file for TM4C123GH6PM processor. the compiler generate many errors until i change the definition type from uint32_t to unsigned long as follow: #define GPIO_PORTF_DATA_R (*((volatile uint32_t *)0x400253FC)) // not work #define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC)) // work good and do this for all pointers that i used . I cann't understand why?
I am getting the same errors, I think unit32_t is declared in stdint.h file. However, I tried to include that file without any success. The compiler gave me error at the stdint.h file. Have you found the read solution? Thanks
Emm but I don't know why I am not able to include stdint.h or use its declaration of unint32_t. However I found a temporary solution which is changing type in the header file from unint32_t to unsigned long. Some people online said it is a wrong solution!
The file is quite essential for embedded programming, so you should be able to use it. At this point, I would highly recommend that you simply download the code for lesson5 from www.state-machine.com/quickstart/lesson5.zip and try to build it on your machine.
I'm not quite sure what this question is about. Yes, I am an active programmer still developing a lot of embedded real-time code. And yes, I intend to continue this course (just please watch the remaining lessons!). It just takes a lot of time to make new videow, but they will be coming. Stay tuned... --MMS
thanks bro we are currently developing a new POS project from C to vb.net that's how i have come across your YB channel i will really love to ask you a question do you mind ! please just for a clarification thanks
I prefer to avoid the use of the pre-processor as much as possible. Using macros can lead to some very hard to find bugs and unmaintainable code. Much better to make use of const, and inline so that the compiler can catch any errors.
+quantumLeaps, LLC: Hi Miro. Thanks a lot for such a nice tutorial! I face to a strange problem; although I can set all registers but the same command structure for only data register doesn't work: GPIO_PORTF_DATA_R = 0x20; !! But it can be nicely set using (*((unsigned int*)0x400253FC)=0x02) command! Do you have any suggestion?! [My board is TM4C123GXL and I already include "tm4c123gh6pm.h" and . ]
I'm not sure which "tm4c123gh6pm.h" header file you use (it is not included in the lesson5.zip project for this lesson), but the "tm4c_cmsis.h" and "TM4C123GH6PM.h" header files included in the later lessons (starting from lesson 12) is based on the CMSIS (Cortex Microcontroller Software Interface Standard). The CMSIS prescribes a different way of accessing registers, based on the C structures, so GPIO_PORTF_DATA_R is NOT defined anymore. Please watch lesson 12, which explains C structs and the CMSIS. --MMS
The header file lm4f120h5qr.h is included in lesson5.zip that you can download from the companion page to this video course at www.state-machine.com/quickstart/ . The downloads for lesson17 and later contain the header file TM4C123GH6PM.h , for the equivalent MCU TM4C123GH6PM, which is equivalent to the LM4F120. --MMS
I recently got hired by a company for a mixed programming position. I had NO experience in embedded systems programming whatsoever and was struggling to catch up. This course single-handedly brought me up to speed with quality and clarity unrivaled by any other UA-cam training course (and I have watched quite a few). If you have just found this course, keep going. You have come to the right place. The fact that Miro Samek has posted this course for free is the height of scholastic generosity. I have learned more from this course than any computer-science college class.
Miro, thank you so much for producing this outstanding embedded programming course. You are so generous to put something so thorough and valuable to those of us wishing to learn embedded programming on UA-cam.
What a great lecture! MACRO and the "volatile" keyword are mentioned briefly in most freshman C programming courses. This lecture reminds me of their importance in firmware programming: define MACRO upon MACRO or fancy MACRO functions, use "volatile" to access MMIO registers or global variables likely modified by interrupts, and go through the headers provided by MCU manufacturers.
These lessons are fantastic. Just wanted to say thanks
This series is put together so incredibly well. It really packs a punch, for someone like me who barely knew the basics of arduino c, this series explains in great depth, and in a way I can actually understand. Thanks to you! Excellent instructor!
Hello, Mr. Miro Samek! I want to thank you very much for making these lessons. It's the best tutorial I found online and it's helping me alot. I am totally excited! I have applied learnt to my STM32F4 board.
Yes, I will devote at least one lesson to the startup code and the "world before C". This will be acutally necessary to explain interrupt handling, because interrupts are defined in the interrupt vector table, which is part of the startup code.
Stay tuned...
--MMS
These days all software is obtained from the Internet, so don't expect any CD-ROMs anymore. Instead, the box with your board contains a small card, where they list a URL from which you can download the code. But, just as well you can simply google for your board, for example type "TivaC LaunchPad" into Google. On the web-page for the board, TI provides the datasheet, other documentation in PDF, and software. Please be warned that the software, which is called TivaWare, is huge. Alternatively, you can go to the companion web-page advertised in every lesson of this course: www.state-machine.com/quickstart , where you can find all the projects for the lessons. Each such project contains the TM4C123GH6PM.h header file. --MMS
21/06/2023 ❤❤
Thank you so much for this great course, i'm learning something new in each lesson! ... Just wanted to say thanks
Fantastic Miro....Your presentations are excellent.A true master.I cannot wait for future lessons.
Miro, your videos are superb and well structured. Thank you.
Best Tutorial ever! I recommend the viewers to follow the series from beginning.
Excellent ! You are doing great thing. All of us encourage you to continue this series.
Preprocessors are important to get familiar with.
Impressive collection of videos. The best I have ever seen. Awesome and well done.
This is the stuff any C tutorial will omit but is absolutely necessary for low level c programming
At the end of each lesson I merely ask to subscribe to the StateMachineCOM channel. By doing this you will get a notification when new videos are posted and you will also help to increase the ranking of the channel on UA-cam, which will place the videos higher in the search. In other words, you will help others to find this channel.
--MMS
Thank you Doctor Miro for this great resource. I was very confused regarding where to start learning Embedded systems programming but this is just perfect. I'm making progress every day.
Hi Miro,
Firstly thank you so much for such a abstract tutorial on embedded systems programming. I have seen a lot of videos online and i have followed some pdf tutorials as well, but none have explained the way you have done. As a beginner in the embedded software world i see you as a first guide in my journey exploring embedded world. You have just cleared blockage in my mind that i had for pointer and dereferencing of the pointers.
Miro, you being such a great tutor and expert in embedded systems, i feel you have to take this a step further in explaining complete modules and its interfaces with the ARM, like : SPI, UART, I2C, CAN, TCP/IP (Ethernet), RTOS and may be guide us to develop a real time system using these concepts.
Please could you also make a video explaining "function pointers" in embedded systems perspective.
Thanks and god bless your family !!
(From the bottom of my heart)
3. My plan is to produce a lesson every two weeks or so.
I'm actually surprised how time consuming the process of producing each lesson is. The most time goes into
editing a lesson down to about 15 minutes.
Great...First time understood why VOLATILE keyword is so important
Thanks so much for those lessons. Wish you a long life
These lectures are really helpful. Thank you.
Thankyouu soo so much. This playlist made me love embedded programming after i hated it for half a semester. You made it easy. So thankyou soo much for your effort❤️
I agree. There are better alternatives to the preprocessor for accessing the hardware registers. Yet, I can't imagine a C programmer unaware of the C preprocessor macros, especially that silicon vendors provide standard header files that still very heavily rely on the preprocessor.
My goal for the future lessons is to use the CMSIS (Cortex Microcontroller Software Interface Standard), which maps hardware registers to C structs. But to get there I need to introduce structures. Stay tuned...
Big thanks for these videos, easy to follow, simple but progressive, thanks!
Thank you , yes BEST video in the real world !
hello, i didn't understand the principle of optimizing the access to a non-volatile objects at 9:26, please i need more explanation.
Did you get an answer to this. Can you please explain me as well?
Superb!!!! The best online Embedded Programming Course I came across ever!!! Can we expect a full pledged embedded C commercial project some time later on the course. This would greatly help the new embedded programmer seeking job :-)
Thanks for your wonderful effort. Awaiting for the great lectures....
simply stellar lessons ! much thanks
You are incredible teacher,congratulation an thanks very very much
Great work. Waiting for future lessons
Awesome lesson. Thanks
danke für die bereitgestellten infos :)
Gern geschehen, --MMS
Great Explanation 😀
Thank you very much for your lessons.
Hi Miro,
Few queries listed below. Advance thanks for your effort. 1. In the IAR project examples, startup.c file is included, but not found in your code. Can you please explain what is role of startup.c.
2. In another example it is found that two header files are included as #include "inc/hw_memmap.h"
#include "inc/hw_timer.h", should we add all those header file into the program or they are built in the package.
3. Eagerly awaiting for the next lectures, when are they coming?
Hello Miro,
Your videos are very helpful. Thanks!!
I would like to know more about the compiler optimizations and the ways to overcome the unnecessary ones.
In addition I would like to know about #pragma directives.
Could you add videos on the above topics.
Thanks
Surya
thankyou for your explaination
if you've got error including #include "lm4f120h5qr.h", the easiest way is to download his project and save it as is and keep on going..
Very informative, I'm using a different chip (Freescale 5554) but I am enjoying this.
If you are using the header file tm4c123gh6pm.h: you might get the error:
Error[Pe020]: identifier "uint32_t" is undefined
If so, you can fix this by also using including this line in your source file:
#include
(this has the typedef for uint32_t, which is used in tm4c123gh6pm.h instead of unsigned long).
Thank you Eric Z
Thanks man, still not working yet but this is a big piece of information.
Thank, Eric. Got stuck for some time.
For anyone who is using tm4c123gh6pm, you can download the header file here: users.ece.utexas.edu/~valvano/Volume1/tm4c123gh6pm.h
Just right-click then choose Save As and that's it
Salvaged Door you beautiful, beautiful person! This really helps!
Hi Mr,
Thank you again for this interesting series of videos, this time I want to ask you about what really happned when you pushed the compiler optimization capabilities to "high"; in fact why the while loop controling the counter variable(delay loop) isn't executed????!
thnaks fo the explanation
best regards
@Quantum leaps, LLC
+khaled gharbi The value of the 'counter' variable is not used anywhere inside the loop, except the counter is incremented. The compiler notices this, and treats the whole loop as a very inefficient way of incrementing the 'counter' by the number of iterations. So, instead of executing the loop, the compiler optimizes it out and sets simply counter = 1000000. Please note that the compiler is allowed to optimize your code that way (unless you declare 'counter' as volatile).
+Quantum Leaps, LLC Good morning Mr, it is clear now , as I understand the compiler optimize the whole while loop by seting the counter to 1000000 and as a fact there is no delay. thanks again sir. :)
Thanks alot for great tutorial!!! One thing i think is missing is the line-numbering for us viewers to find stuff faster :D
why at high optimization level the loop is not encountered ? why the last value of counter value is ignored with out using volatile ?please any one can answer ?
You need to look at the code from the compiler's perspective.l A loop like "while (counter < 1000000) { ++counter; }" is to the compiler a horribly inefficient way of changing the "counter" variable from 0 to 1000000. Therefore, the compiler might "optimize" it to "counter += 1000000", or if "counter" is not used afterwards, the compiler can completely eliminate the whole loop. --MMS
2. As you can test for yourself, the project files for the lessons don't include any inc/hw_... files and they work, so you don't need to include them. I will clearly state when any external file is needed and I'll try to explain what it brings.
But if you want to understand what these files contain, you should be able already to do so. For example inc/hw_memmap.h contains base addresses of various memory blocks from the LM4F Data Sheet, that I've discussed in Lesson-4.
Crystal Clear
Thank you for the lesson. But I don't understand why 400253FC is located in the right most column in the memory window? Thank you.
If you get an error about "uint32_t" not being define, #include
Thank you!
Love your videos. I'm following this tutorial with a STM32F4 board and you videos really make me understand what is going on inside the MCU. One question however, where can you get Header files, I've been googling for some time now and can't really find anything other then people who made them themselves.
CMSIS-compatible header files for the STM32 family of MCUs are provided in the STM32Cube library from ST. Just search for "STM32Cube".
Thanks, I was also struggling with the same question.
Awesome! Thanks a lot.
From where did you get that the GPIO DATA register is of offset 0x3FC? In the data sheet it is written to be 0x000. Thanks!
A short explanation is:
The data register in fact does not reside in only one address but instead it is mapped to 256 different addresses. One of them is at offset 3FC (another one is 0x000 which I will use as an example to their difference below). According to the datasheet, only bits [9...2] (8 bits) of these addresses are considered and used (or we could say that each address in general is used) to allow reads and writes to a certain permutation of the data register which also has only 8 bits in use (see the datasheet). (for these [9..2] bits in an adress, 1 means i/o to the corresponding bit in the data register is enabled, likewise 0 means i/o is disabled)
For example if you write data to address with offset 0x3FC which is: 0000 0000 0000 0000 0000 0011 1111 1100, its bits [9...2] = 1111 1111 => all bits you write to this address will be written to the corresponding bits in the data register too.
If, however, you write the data to offset, let's say, 0x000 whose bits [9...2] is 0000 0000 then the data register will NOT be affected no matter what you write into this address.
The purpose of this isolation in bits is that GPIO ports usually comprise many pins each and often we would want to perform io on individual pins without affecting others. However, there is only one data register for each GPIO that is shared between pins, e.g. bit 1 of the data register is mapped to pin 1, bit 2 is mapped to pin 2 etc so we need a method like this to be able to perform io exclusively on each of these pins (or a certain group of pins).
I think you might want to read the description of the data register in the Register Description section which offers a more thorough explanation. Pay close attention to the part where it describes the memory layout of the data register and how to read from and write to it.
You can find the explanation in lesson 7
I also got confused about this. I ended up finding the article below:
filderbaer.wordpress.com/2014/12/30/2-gpio-programming/
To hopefully help drive the point home, the tutorial instructs us to write 0x0/0x2 to (GPIOF_BASE + 0x3FC) to turn the LED on and off. Another way to turn the LED on and off is to write 0x0/0x2 to (GPIOF_BASE + 0x008).
6:53 this sums up video #4
Great video. Thanks. Where can I download this header file?
All project files (including all code) is available online at the companion web-page to this video course: state-machine.com/quickstart
thank you so much
Thanks for video
I have a question about the coloring. I can see that the whole preprocessor directives line in your case is colored blue. On the other hand if I use my IAR Embedded Workbench than only " #define " is colored blue and all after the statement is black and green as rest of the code. Is this an option you can change or is something wrong with my IAR Workbench?
you sir. are awesome
you are the best
sir as you have said that ti provides header file , but there is nothing i have received other then board and cable in a box , can you tell me how to get that file
Hi Miro, great lesson! I had a question about the compiler optimization. I am running a slightly modified version of the blink code where I alternate between r,g,b for the led after every blink. I noticed that after changing the optimization setting to high and making the relevant variables volatile, the led blinks at a higher rate than when optimization is turned off. When I track the counter variable step by step, I find that it is essentially doing counter+=4 each iteration. How does the compiler decide to do this?
The C compiler is allowed to apply various optimizations to your code, which you apparently experience in this case. The rules of what the compiler is and isn't allowed to do are a bit nuanced, but please search the web for "sequence points in C". The busy-wait delay loops are especially vulnerable to optimizations. To be really stable, such loops shouldn't be based only on the CPU. Instead the delay loops should be based on checking some hardware timer module, which is known to run at a specific frequency. --MMS
You are awesome !!!
Thank you so much for this great course, i'm learning something new in each lesson!
I have a question about addressing in assembly by using the PC.
Say i have currently stopped the execution on the instruction at address 0x42:
stopped here --> 0x42: 0x490d LDR.N R1, [PC, #0x34]
(next instruction) 0x44: 0x6008 STR R0, [R1]
I know it corresponds to loading R1 with the value 0x400fe608, as correctly happens, but I don't understand how...
When i am stopped at the instruction above, I have:
PC = 0x00000042
and the addresses which are closer to the "target" one are:
0x72: 0x1c40 ADDS R0, R0, #1
0x74: 0xe7fa B.N 0x6c
0x76: 0xbf00 NOP
0x78: 0x400fe608 DC32 0x400fe608 (1074783752)
The PC-based addressing is made a bit complicated due to the pipeline effects. (Instruction pipeline is touched upon in Lesson 2.). But basically, by the time the PC-based LDR instruction executes, the PC is already incremented to the *next* instruction. In programming practice this is not an issue, because you never calculate the PC offsets by hand. All assemblers provide some help, such as LDR r0,=0x400fe608 will generate a PC-relative load for you.
I had the following warning after adding the macros functions, but the code still works as expected. i just need to be sure that it's not a problem?
Warning[Pe1053]: conversion from integer to smaller pointer
Hello Miro,
You recommended that the volatile keyword should be used after the type 'int'. Any particular reason for this?
Thank you for these videos.
tulsidas123 IMHO: When scanning through the code, you are normally more interested in looking for a particular type of variable than whether or not it is volatile or not. Hence, placing the variable type first allows you to scan vertically through your source for instances of "int" instead of "volatile int"
tulsidas123 My recommendation for placing 'volatile' (as well as 'const') after the type comes from the C/C++ guru Dan Saks. Here are a couple of links to his articles that explain the matter (please note that the same discussion applies to both 'volatile' and 'const' qualifiers):
www.dansaks.com/articles/1999-02%20const%20T%20vs%20T%20const.pdf
www.dansaks.com/articles/1996-12%20Mixing%20const%20with%20Type%20Names.pdf
Hi, does is posible to chanthe SP and LR register in C language? I need it for multitask systems.
Thanks for video. I wanted to ask what would be the value at GPIOF_DATA in case that we want to define inputs instead of LEDs that are outpus? Thanks in advance!
I want to learn to use the TM4C123GH6PM.h file given on state-machine but it seems radically different from the one used in this video. How is this new one intended to be used?
Edit 2: Watch episode 12 and it will cover it!
Edit 1: Hopefully he can correct or confirm this but here's what I've found for those who look at this in the future
The "tm4c123gh6pm.h" file makes types for various register maps, and defines the base addresses it needs including the GPIO ports. It casts each base address as the corresponding type, with data members corresponding to things like GPIOF_DIR. To access something like this you need to access the data member in the GPIOF object, so you would say (*GPIOF).DIR to dereference the GPIOF pointer and set the DIR value. The -> operator is a shorthand for dereferencing a pointer to an object and then obtaining a data member of that object, so you could instead put GPIOF -> DIR which is cleaner and preferred. Your code should read " GPIOF -> DIR = 0x0EU; " (without the quotes of course, and actually a value like 0xE works fine too because it's already defined as an unsigned integer and 0xE will translate as 0x0...00E not 0xE00...0). Also, the header file depends on the "core_cm4.h" file which is from the CMSIS library. You can find this specific file on github but it's easier to go to project -> options -> library configuration and enable "use CMSIS."
The most confusing part of this to me was "How does the computer know where the address of something like GPIO DIR is, when that value is nowhere in the file?" I suspect that the answer is that it is stealthily encoded into the way the object is made. We take the base address and cast it as a pointer to a GPIO object. This object will hold each data member in memory, starting from the base address of the pointer. The memory locations of these data members like DIR or DATA are at the proper offset because the data members before them take up an amount of memory equal to their offsets. To test this I tried to calculate the address of GPIO DIR knowing only the base address and looking at the code in the header file. We know that a 32 bit unsigned integer takes up 4 bytes of space. The first data member in GPIO is an array of 255 32 bit uints, which means we would expect the next data member, which is DATA, to be 4*255=1020 spaces away, or 3FC in hexadecimal. This matches the offset of DATA. The DATA member is one 4 byte integer, so we would expect the DIR member to be 4 bytes away. 3FC + 4 is 400 in hexadecimal, which matches the offset we had for DIR.
It feels a little weird that the memory locations are not explicitly defined, and instead we depend on the way the object's data members are held in memory, but it does work and it's a pretty cool trick. Hopefully this explanation is clear and helps anyone else who is as confused as I was.
Why aren't you using the human-readable names instead of the physical address?
In the datasheet, the offset for the data reg of port F was 0x00000000. Couldn't find 0x3FC as the offset at all. Am I missing something?
Do you have the answer of this question? I have confused at the same part.
I have a question to the memory addresses. I understand the principal of Clock Gating and also the GPIOF_DIR to set the pins as outputs but what exactly is set with the GPIOF_DEN?
Also I would like to know if that procedure would apply to other embedded systems?
Thanks!
The "GPIOx_DEN" register stands for "Digital Enable". The purpose here is to configure the selected GPIOx pins to be used for digital input/output. This is necessary, because pins coming out of the package are a very limited and precious resource (in fact, pins cost more than the silicon inside). Therefore, pins have multiple functions (e.g., analog, receive/transmit, PWM, etc.), which can be selected in software. Please refer to the TivaC Datasheet, Chapter 10 "GPIO" for more information.
Thank you for your answer, it makes more sense now.
Hi,Sir
1.When would the counter discard?
2.How to sure the bug is caused by optimization or not,especially the normal variable?
3. If I use a timer interrupt func. which has a global variable in it, dose the optimization still would affect the code?
+thentust
1. The compiler is always free to optimize your code, so you cannot assume that a non-volatile counter will be in fact incremented in a slow and very inefficient way (from the compiler's point of view).
2. You simply need to know that the compiler might optimize your code. Therefore, without the 'volatile' keyword your code is incorrect. It just so happens that the code works at lower levels of optimization, but this is an accident. It is not the compiler's problem. It is *your* problem.
3. Global variables inside interrupts are tricky, because they are subject to *race conditions*. I will discuss these issues in the upcoming lesson 20. Stay tuned...
how to add a header file???
Why do you think it is better to write "volatile" after type declaration? Thanks for this great tutorial :D
By placing "volatile" or "const" after the type, as opposed to the usual placement before the type, I follow the recommendation of Dan Saks. He explained the reasons in the article "const T vs. T const": www.dansaks.com/articles/1999-02%20const%20T%20vs%20T%20const.pdf . --MMS
Thanks. There is also a discussion about use of #define vs const. Which one is better and safer for embedded design?
Generally, any alternative to the preprocessor (#define) should be preferred in the code. This is because pre-processor macros are expanded *before* the C compiler has a chance to analyze the code, to the compiler cannot perform syntactic analysis on the macros before their expansion. Also, macros can have unintended side effects. One alternative to #define is providing integer constants as enumerations (enum {...}). --MMS
Hן
Where I find th macro file mentioned in this video ?
Elico
All the source code and the complete projects are available for download from state-machine.com/quickstart.
--MMS
That link is broken please check
Elico, UA-cam makes it hard to post web links, so please don't expect that you can just click on the link. But, please just take a look at the link in your browser and, if you know anything about URLs, I hope you would see why it doesn't work. I hope it will be obvious to you how to get to the quickstart page. This is the general attitude I would highly recommend in programming--try to think why something doesn't work instead of expecting everything served to you on a plate.
--MMS
But how is the compiler exactly messing the variable up? From what is understand the variable is registered to cpu memory and then the compiler places it back to a different ram memory adress or the compiler doesnt place it at all? What is exactly going on?
Actually, the counter variable does not have any memory address before it is declared 'volatile'. The variable is placed in a register. But the really important point here is that the whole delay loop looks to the compiler as a very inefficient way to change 'counter'. Instead of changing 'counter' one count at a time, the compiler is free to optimize it to just one big change. It is like you would deposit $1,000,000 to your bank account by writing million checks for $1. Instead of this, you could simply write one check for $1 million. But then the compiler also notices that you never use the `counter` again, so it optimizes the whole thing away. To prevent all this from happening, you need to declare 'counter' as volatile.
Iar workbench is not running in my pc... showing some file is missing after installation.can i use keil uvision 5 ??
The ARM-Keil uVision IDE (a.k.a. ARM-MDK) is introduced in lesson 21 (ua-cam.com/video/AoLLKbvEY8Q/v-deo.html ). --MMS
I am using Tiva C series Launchpad and during the Project develop, I also chose 'TI Stellaris' under the debugger/setup/driver, however, when I download the code, it pops the driver warning and therefore the IDE doesn't perform as expected. Does someone in the community also experience such problem and please give me some tips, regarding how to solve it.
Please visit the companion page to this video course at www.state-machine.com/quickstart. Among others, you'll find there the document "Troubleshooting TivaC LaunchPad" in PDF. You can also find there the "Tiva/Stellaris USB Drivers" to update on your machine. --MMS
Hi, I have a question. I know you provided the header file "lm4f120h5qr.h". Where do you download this file from? I ask because I may be using a different board and would like to know where to get the header file for a Different processor.
Please google for lm4f120h5qr.h. --MMS
Hey QP, sorry I'm struggling to understand how 0x0EU sets 1,2 and 3 to output. I'm looking at the data sheet for 0x40025400(GPIO_DIR), on page 660 it says that 0 is input and 1 is output. Not sure how E sets 3 pins to output.
+Helios Fire Remember that bits are numbered from zero, so bit 1 corresponds to 0x02, bit 2 to 0x04 and bit 3 to 0x08. Now, to set all three of these bits, you can create a bitwise OR like this: (0x02 | 0x04 | 0x08) == 0x0E. I hope this helps.
Quantum Leaps, LLC
Thanks for the explanation QP, I'm just wondering where I find that information on the datasheet as it only gave me 0 as a input and 1 as an output. If I wanted to set a specific GPIO pin to an output, where would I find it in the datasheet?
+Helios Fire To get a quick overview of the registers for a given peripheral, such as GPIO, you can look for the "Register Map" section. For example, the "GPIO Register Map" starts on page 657. Typically, a "register map" contains only offsets from the base register, because the registers are repeated in every instance of the peripheral, such as GPIOA, GPIOB, ... GPIOF, etc. (The C language offers structures, with which to model the offsets in the code. This concept is discussed in the upcoming lesson 12.)
From there, you can see that the GPIO direction is controlled by the GPIODIR register at offset 0x400. The datasheet list the page #, where you can read about this particular register. Again, I hope this helps...
Thanks it does, slowly getting it everyday
I love your teaching i really understands you perfectly but i want to ask you favor to explain stm32f4-discovery board with keil uvision5 software especially LED and LED matrix
Is there a similar header file of Macros for when I want to program directly in assembly? Or do I need to manually key in the entire register address everytime while programming in assembly? Also, how does the 'volatile' keyword manifest itself in assembly language?
It only manifests itself int the way, that the assembly code will be less optimized. There is not volatile machine instruction, it's something that only the C compiler sees.
its good to have straight A's
dear sir
I tried to include the header file for TM4C123GH6PM processor. the compiler generate many errors until i change the definition type from uint32_t to unsigned long as follow:
#define GPIO_PORTF_DATA_R (*((volatile uint32_t *)0x400253FC)) // not work
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC)) // work good
and do this for all pointers that i used .
I cann't understand why?
I am getting the same errors, I think unit32_t is declared in stdint.h file. However, I tried to include that file without any success. The compiler gave me error at the stdint.h file. Have you found the read solution? Thanks
The file is part of the C99 standard library. It should be available in all C99 compilers. IAR EWARM certainly provides it.
Emm but I don't know why I am not able to include stdint.h or use its declaration of unint32_t. However I found a temporary solution which is changing type in the header file from unint32_t to unsigned long. Some people online said it is a wrong solution!
The file is quite essential for embedded programming, so you should be able to use it. At this point, I would highly recommend that you simply download the code for lesson5 from www.state-machine.com/quickstart/lesson5.zip and try to build it on your machine.
GPIOF_DATA = 0x02u
what does the value 0x02u refer to ?
+yasser el-azab it means 2, the u at the end implies that the value is unsigned
are you still please active in programming
I'm not quite sure what this question is about. Yes, I am an active programmer still developing a lot of embedded real-time code. And yes, I intend to continue this course (just please watch the remaining lessons!). It just takes a lot of time to make new videow, but they will be coming. Stay tuned... --MMS
thanks bro we are currently developing a new POS project from C to vb.net that's how i have come across your YB channel i will really love to ask you a question do you mind ! please just for a clarification thanks
On a 32 bit machine, unsigned long = unsigned int
I prefer to avoid the use of the pre-processor as much as possible. Using macros can lead to some very hard to find bugs and unmaintainable code. Much better to make use of const, and inline so that the compiler can catch any errors.
+quantumLeaps, LLC: Hi Miro. Thanks a lot for such a nice tutorial! I face to a strange problem; although I can set all registers but the same command structure for only data register doesn't work: GPIO_PORTF_DATA_R = 0x20; !! But it can be nicely set using (*((unsigned int*)0x400253FC)=0x02) command! Do you have any suggestion?!
[My board is TM4C123GXL and I already include "tm4c123gh6pm.h" and . ]
I'm not sure which "tm4c123gh6pm.h" header file you use (it is not included in the lesson5.zip project for this lesson), but the "tm4c_cmsis.h" and "TM4C123GH6PM.h" header files included in the later lessons (starting from lesson 12) is based on the CMSIS (Cortex Microcontroller Software Interface Standard). The CMSIS prescribes a different way of accessing registers, based on the C structures, so GPIO_PORTF_DATA_R is NOT defined anymore. Please watch lesson 12, which explains C structs and the CMSIS. --MMS
Can someone navigate me to lm4f120h5qr.h file please?
The header file lm4f120h5qr.h is included in lesson5.zip that you can download from the companion page to this video course at www.state-machine.com/quickstart/ . The downloads for lesson17 and later contain the header file TM4C123GH6PM.h , for the equivalent MCU TM4C123GH6PM, which is equivalent to the LM4F120. --MMS