|
3 | 3 | [](https://opensource.org/licenses/MIT) |
4 | 4 | [](https://github.com/cpq/bare-metal-programming-guide/actions) |
5 | 5 |
|
| 6 | +[English](README.md) | [中文](README_zh-CN.md) |
| 7 | + |
6 | 8 | This guide is written for developers who wish to start programming |
7 | 9 | microcontrollers using GCC compiler and a datasheet - nothing else! The |
8 | 10 | fundamentals explained in this guide, will help you understand better how |
@@ -156,6 +158,8 @@ from A0 to A15, to input mode: |
156 | 158 | * (volatile uint32_t *) (0x40020000 + 0) = 0; // Set A0-A15 to input mode |
157 | 159 | ``` |
158 | 160 |
|
| 161 | +> Note the `volatile` specifier. Its meaning will be covered later. |
| 162 | +
|
159 | 163 | By setting individual bits, we can selectively set specific pins to a desired |
160 | 164 | mode. For example, this snippet sets pin A3 to output mode: |
161 | 165 |
|
@@ -576,6 +580,11 @@ uses a configuration file named `Makefile` where it reads instructions |
576 | 580 | how to execute actions. This automation is great because it also documents the |
577 | 581 | process of building firmware, used compilation flags, etc. |
578 | 582 |
|
| 583 | +There is a great Makefile tutorial at https://makefiletutorial.com - for those |
| 584 | +new to `make`, I suggest to take a look. Below, I list the most essential |
| 585 | +concepts required to understand our simple bare metal Makefile. Those who |
| 586 | +already familiar with `make`, can skip this section. |
| 587 | + |
579 | 588 | The `Makefile` format is simple: |
580 | 589 |
|
581 | 590 | ```make |
@@ -856,13 +865,24 @@ every millisecond. We should have interrupt handler function defined - here |
856 | 865 | it is, we simply increment a 32-bit millisecond counter: |
857 | 866 |
|
858 | 867 | ```c |
859 | | -static volatile uint32_t s_ticks; |
| 868 | +static volatile uint32_t s_ticks; // volatile is important!! |
860 | 869 | void SysTick_Handler(void) { |
861 | 870 | s_ticks++; |
862 | 871 | } |
863 | 872 | ``` |
864 | 873 |
|
865 | | -And we should add this handler to the vector table: |
| 874 | +> The `volatile` specifier is required here becase `s_ticks` is modified by the |
| 875 | +> interrupt handler. `volatile` prevents the compiler to optimise/cache |
| 876 | +> `s_ticks` value in a CPU register: instead, generated code always accesses |
| 877 | +> memory. That is why `volatile` keywords is present in the peripheral struct |
| 878 | +> definitions, too. |
| 879 | +
|
| 880 | +Note the `volatile` specifier for `s_ticks`. Any variable that is changed |
| 881 | +by the IRQ handler, must be marked as `volatile`, in order to prevent the |
| 882 | +compiler to optimize the operation by caching its value in a register. |
| 883 | +The `volatile` keyword makes generated code always load it from memory. |
| 884 | +
|
| 885 | +Now we should add this handler to the vector table: |
866 | 886 |
|
867 | 887 | ```c |
868 | 888 | __attribute__((section(".vectors"))) void (*tab[16 + 91])(void) = { |
|
0 commit comments