Welcome to the Modern Embedded Systems Programming course. My name is Miro Samek and in this lesson I'll talk about the C structures. I will also introduce you to the Cortex Microcontroller Software Interface Standard (CMSIS), which uses structures to access hardware in Cortex-M microcontrollers. As usual, let's get started with making a copy of the previous "lesson11" project and renaming it to "lesson12". If you are just joining the course, you can download the previous projects from state-machine.com/quickstart. Get inside the new "lesson12" directory and double-click on the workspace file to open the IAR toolset. If you don't have the IAR toolset, go back to "lesson0". The latest version published by IAR is 7.10, which I'm using in this lesson. So far in this course, you have been only using the so called scalar data types, such as the built-in integers or the exact-width integer types introduced in the last lesson 11. In lesson 7, you also learned about arrays, which is a way to group together variables of the *same* type. For example, array a contains 32 identical uint32_t integers. Structures are another mechanism in C to group together variables, possibly of *different* types. The benefit of structures is that they permit a group of related variables to be treated as a unit instead of as separate entities. In embedded systems, structures also permit you to access hardware in an elegant and intuitive way. I will explore this aspect later in this lesson. But first, let me explain the syntax and give you a few examples of structures in C. For this I will create a few structures for an embedded graphic LCD display. The basic object is a point, which groups together an x coordinate and a y coordinate. The keyword struct introduces a structure declaration. An optional name called a *structure tag* may follow the word struct (as does the tag Point in this example). The variables enlisted between the braces in a structure are called members, so here x and y are members of struct Point. On a horizontally elongated LCD display, x might need a bigger dynamic range than y, so let's make x uint16_t and y only uint8_t. A structure declaration can be immediately followed by a list of variables. So for example, you can define variables pa and pb, both being Points. Actually, if you define variables immediately after the structure, you might omit the structure tag altogether. I press F7 to quickly show you that the compiler accepts this form just fine. However, more common is to define a structure followed with an empty list of variables. This is then the only situation in C, where the closing brace must be immediately followed by a semicolon. A structure declaration that is not followed by a list of variables reserves no storage, but if the structure is tagged, you can use it later to declare variables as follows. Nonetheless, this way of using structure types is still a little cumbersome, because you must always repeat the struct keyword in front of the structure tag. This is also not how it is in C++, where you don't need to repeat the keyword struct or class in front of all of class names. But by now you should know the solution. In the previous lesson you learned about the typedef directive, which you can use to define the Point type as follows: Now, you can define variables p1 and p2 of type Point without the keyword struct in front. An interesting wrinkle here is that, as you can see, the compiler accepted the same name Point for both the struct tag and the typedef name. This is because tag names in C occupy a different namespace than typedef names, variable names, or function names. Interestingly also, you can place the typedef even before the struct declaration, and the compiler still likes it. Finally, there is a way to remove the repetition of the Point name altogether by using the un-tagged struct declaration inside the typedef. In this last case the compiler obviously no longer recognizes the 'struct Point' type, but it still likes the typedef'ed Point type. In summary, my preference is to use the last typedef form without the struct tag. Actually, this is also recommended by the latest safety C standard called MISRA:C-2012, where the advisory rule 2.4 recommends that a project should not contain unused tag declarations. Struct tag names are almost never needed. The notable exception being the so called self-referential structures, such as nodes of linked lists or trees. But this is an advanced way of using structures, which goes beyond the scope of this course for the time being. So now, that you know all the different forms of declaring structure types, it's time to find out what you can actually do with them. Well, the first thing you can do is to get access to the individual struct members. As usual in C, this is achieved by the special "member access" operator, which is a dot. For example, here you obtain the size of the structure Point and assign it to p1 dot x member. Of course, you can also use the '.' operator inside an expression. Here, for example, you calculate p1.x member minus unsigned 3 and assign it to p1.y member. At this point you need to know that the '.' operator has a very high precedence, which is higher than any arithmetic operators, such as '-'. That's why you typically don't need to use parentheses around the access to struct members. So now it's finally time to step down to the machine code and see how your Point structure actually looks in memory and what's its size, because it is not three bytes--2 bytes for x and 1 byte for y--as you might expect. For this part of the lesson, I'm going to use the Simulator, but you can also run the code on your LauchPad board. I cleanup the Watch 1 view and prepare it to watch the p1 structure variable. I also setup the Memory view to show the memory around the address of p1. As you can see in both views, p1 starts with all zeroes. When you step through the code, you can see that the compiler treats p1.x as any other half-word variable and uses the already familiar STRH instruction to store a value in it. But the compiler evidently thinks that the size of the Point struct is 4 bytes, not 3. In the evaluation of the expression, you can see again that p1.y is treated as a byte-size variable, because the result of the expression is stored with the STRB instruction. Looking in the memory view, you can now recognize the Point structure as the *four* bytes at address 0x2 followed by all zeroes. The p1.x member with value 4 is at the beginning of the structure, and p1.y member, with value 1 is immediately after it. Interestingly, the compiler has padded the structure by one byte after the p1.y member. To investigate the structure layout and padding, let's reverse the order of members and run again. As you can see, the order of members has also been reversed in memory, because p1.y is now at a lower address than p1.x. Looking in the memory view now, you can see that indeed the p1.y member at the beginning of the structure is followed by one unused byte before the p1.x member, so that the total structure size is 4 bytes. Compared to the previous run, this change of order should convince you that the compiler will honor the order of structure members, exactly as you typed them in the structure declaration. However, the compiler can and sometimes will insert extra padding bytes into your structure. The second interesting observation is that evidently, the compiler for ARM Cortex-M preferred to waste one byte of memory rather than place the p1.x half-word member at an odd address. The obvious question is why? To investigate this last question, it would be interesting to somehow force the compiler not to waste the byte between the y and x members. This is not possible in the standard C, but most embedded compilers provide some non-standard extension to pack the structure members tightly, without any padding. The IAR compiler, provides for this an extended keyword __packed, which you can place in front of the struct keyword. So, let's do this right now and see what happens. Also, to avoid value zero in p1.y, which will happen when the size of the struct will indeed be 3, let's change it to something else. The first interesting thing to notice in the Watch view is that this time around the p1.x member is allocated at an odd memory address. In the disassembly window notice that the code is still very efficient. In particular, the assignment to p1.x is still handled by the STRH instruction, even though p1.x is at the odd address. So, what's the big deal? The CPU handled the packed structure just fine. To see a real difference, let's change the CPU... Open the project options dialog box, go to General options, and select the Cortex-M0 core instead of Cortex-M4F. Next, go to the Debugger section and select the TI Stellaris interface and make sure that the "Use flash loaders" option is checked. Yep, that's right. I really intend to run the code not just in the Simulator, but on real hardware. If fact, I will run on the Tiva-C LaunchPad board, even though it has the Cortex-M4F processor, not Cortex-M0. This way, I hope to convince you that Cortex-M processors are truly binary compatible. Specifically, from the chart of Cortex-M machine instructions, you can see that Cortex-M4 recognizes all instructions for Cortex-M3, which recognizes all instructions for Cortex-M0/M1. This is like a set of nested dolls, where any bigger doll can hold all the smaller ones inside. After the code is programmed into the LauchPad board, quickly check that p1.x is still at the odd address and setup the memory view. When I start stepping through the code, notice that the code for the simple assignment of p1.x is bigger than before. To help you see the difference, I put the previous disassembly for Cortex-M4F side-by-side. As you can see, the same C source code compiled for Cortex-M0 consists of two STRB instructions plus a logical-shift-right instruction, whereas Cortex-M4 achieved the same effect with just one STRH instruction. Interestingly, this is not because Cortex-M0 does not have the STRH instruction. In fact it does, but it can't use it at this point, because the STRH instruction in Cortex-M0 is less powerful than in Cortex-M4 and cannot access a half-word allocated at an odd address. So here for the first time you see that alignment of the data in memory matters to the processor. As an embedded programmer you absolutely need to know about it, because otherwise you will never understand why the compiler inserts padding into your structures. The compiler prefers to keep the data aligned instead of wasting the CPU cycles to access the mis-aligned data. You can also see here, that packed structures might not be as efficient to access as the regular, un-packed structures. In other words, you need to use packed structures judiciously only when you absolutely need to avoid padding. To conclude the experiment of running Cortex-M0 code on Cortex-M4 CPU, I just run the program so that you can see that the LauchPad board keeps blinking the LED as before, so it haply executes the M0 code. So now, I would like to show you a few more things that you can do with structures. First, you can make more complex structures, including structures that contain other structures. For example, a struct Window might contain two points for top_left and bottom_right corners of the window. Structures can also contain arrays, for example, a structure Triangle might contain an array of three corners, each one being a point. So you can see here that you can have arrays of structures. The variables of structure types are sometimes called "instances", so here 'w' is an instance of Window and 't' is an instance of Triangle struct. Now, regarding accessing members of such complex structures, here are examples of accessing nested members of the Window and Triangle structures. Please note how the IAR editor helps you by displaying all possible members of a given structure. But assignment is not limited just to the basic scalar types. You might also assign whole structures. For example, you can assign the whole p1 structure to the p2 structure, or the whole complex Window structure to another Window structure. However, I hope you realize that more complex structures can occupy considerable memory, so an innocently looking assignment of structures can mean copying a sizable chunk of memory from one variable to another. Therefore, instead of copying entire structures from one place in memory to another, it is often more efficient to use *pointers* to structures. As you perhaps remember from Lesson 3, a pointer type is created by simply adding a star after the type name. This is exactly how you make pointers to structure types as well. For example, pp is a pointer to the Point type, while wp is a pointer to the Window type. To initialize a pointer you can take an address of a variable with the address-of operator. For example, you can set pp to point to the p1 Point, or wp to point to the w2 Window. The next question is how to access the members of a structure using a pointer. The C language provides two ways. First, as you learned in Lesson 3, you can apply the star operator in front of a pointer to obtain the object pointed to by the pointer. So, if pp is a pointer to Point, *pp is the Point type. From there you can access any member using the dot operator. Note that the parentheses around *pp are necessary, because the precedence of the '.' operator is very high, even higher than the '*' operator. In a similar way, you can de-reference the wp pointer to set the top_left member. Notice that here again you can see an assignment of whole structures, because both top_left member and *pp are Points. But pointers to structures are so frequently used in C that the language offers an alternative operator 'arrow' as a quicker way to access a structure member via a pointer. So, the following two assignments are equivalent to the previous versions. Let's now take a look in the debugger to see how these more complex structures are accessed. You can keep the Cortex-M0 core, but remove the packed extended keyword from the Point structure to avoid muddying the picture with accessing misaligned data. Step through the code to where the Window structure is accessed and put the w variable in the Watch window making sure that all the members are expanded and visible. Now single-step through the disassembly and note a few interesting things. First, you should notice the STRH instruction, which means that it is available in Cortex-M0, but only when the Point member x is aligned. Second, please note the square brackets after the STRH instruction. This means that the content of the R0 register must be stored at the address in R1 plus the offset of 2 bytes in this case. This addressing mode with additional offset from a given base register is very convenient for accessing structures. In fact, it has probably been designed specifically for this purpose. Because, a structure for the compiler is nothing else but a bunch of offsets, one for each member, from the beginning of the structure. Here again you can see this addressing mode used in the STRB instruction to access the y member of the bottom_right point in the Window struct. As you can immediately see this member has the offset of 4 bytes from the beginning of the Window structure. Let's confirm these offsets by looking at the address of the w variable in the memory view. So here is the whole structure here is the 2-byte offset to top_left.x member and here is the 4-byte offset to bottom_right.y member. So, now I hope you are really getting ready for the idea of applying structures to access hardware registers in an embedded microcontroller, such as the SYS-control registers to enable or disable various peripherals, or the GPIO registers through which you can turn the various color LEDs on and off on your LanuchPad board. Please note that the way you have accessed the hardware registers so far was quite primitive and did not involve structures. You used the lm4f header file, which defined the hardware registers of your LM4F microcontroller simply as preprocessor macros. For example, the GPIO_PORTF_AHB_DIR register was defined as de-referencing of a pointer with the hardcoded address of the register from the datasheet. All other registers were defined in a similar way. In contrast, the idea now is to design a C structure in such a way that its data members would correspond to all the registers within a given hardware block, such as SYCTL or GPIO. For that, you need to consult the datasheet. Here, I have the the datasheet of the specific TM4C microcontroller used on your Tiva LauchPad board, which is also identical to the LM4F microcontroller used on the slightly older LM4F LaunchPad. This PDF is available either directly from Texas Instruments or from the state-machine.com/quickstart web-page accompanying this video course. For example, in the description of the General-Purpose Input/Output block, you can find the section "Register Map". As you can see there, all registers within the GPIO block are specified as offsets from the base address of the block. The register map provides thus a blueprint of the GPIO structure in C, because as you recall from the debug session a minute ago, a structure is just a bunch of offsets, one for each of its members. So, given the datasheet, I hope you start to see how a C structure for the GPIO block can be written. The good news is that you don't need to do it, because it has already been done by the microcontroller vendor. In fact, to your current project directory, I have copied the tm4c_cmsis.h header file, which contains definitions of structures for all hardware blocks found in your microcontroller. When you open the tm4c header file and scroll down a bit, you can see that it contains typedef'ed structure definitions. For example, here you can see the structure for the System Controller--SYSCTL. And right below you can see the structure for the GPIO. As a quick exercise, let's compare the structure definition in the tm4c header file with the GPIO Register Map in the datasheet. As you can see the first register in the datasheet is GPIODATA, but when you look closer, you can see that there is a big gap of 400 hex in the offests to the next register in the map. This is because GPIODATA is not a single register, but rather a group of 256 4-byte registers, which I discussed in detail in Lesson 7. In the C structure, the group of 256 DATA registers is represented as an array of 255 Data_bits member plus the DATA structure member. Again, please refer to Lesson 7 to see why this last DATA register is special. All the following registers in the GPIO map have a one-to-one correspondence to the members of the C structure in the header file. I only wanted to point out the gap in the offsets after the GPIO Alternate Functin SELlect register. Such a gap is represented in the C struct as the RESERVED1[] array member, so that the following members align exactly at the offsets specified in the datasheet. Finally, I'm sure that you are wondering about the types used in the C structure. I hope you recognize the uint32_t fixed with integer type, which means that all the registers are 32-bit wide. But the __IO, __I and __O identifiers surely look strange. As it turns out these are the preprocessor macros defined in the Cortex Microcontroller Software Interface Standard (CMSIS), which the tm4c header file is part of. I will show you the actual definitions of these CMSIS macros in a minute, but first just notice that the macros correspond to the third column in the datasheet, whereas IO, which stands for Input/Output, corresponds to Read/Write. I, which stands for Input, corresponds to Read-Only, and O, which stand for output, corresponds to Write-Only. Going back to CMSIS, you can see that the tm4c header file is not completely standalone, but rather it includes the core_cm4.h header file. This header file is part of the CMSIS industry standard and IAR distributes it as an integral part of the toolset. To see the core_cm4.h header file, go to the installation directory of your IAR toolset, get into arm, CMSIS, Include, and open the file. So, here are the definitions of the macros __I, __O, and __IO. These macros turn out to be defined as the volatile keyword, which I hope makes sense to you. It also means that the volatile qualifier can be used for individual structure members. The __I macro has additional qualifier const, which means that such designated variable is constant and cannot be modified. I hope to cover the use of the 'const' keyword and the concept of const-correctness of programs in one of the future lessons. To finish with the core_cm4 header file, I only want to show you that it too contains structures that define hardware registers. Here for example is the structure for the Nested Vectored Interrupt Controller (NVIC), which is part of every Cortex-M core and therefore it make sense to define it in the CMSIS core header file. You will use the NVIC in the future lessons about interrupts. With all this information, you can finally re-write your code to use the CMSIS-compliant way of accessing hardware using structures. The last piece of the puzzle you need to understand is how to make sure that the SYSCTL or GPIO structures are at the right base addresses. This might seem like a big problem, because so far you have only created instances of Point, Window, or Triangle structures, where the compiler controlled their placement in memory. But this problem is not really new, and you have encountered it before, already in Lesson 4. The solution is to use *pointers* to structures initialized to the hard-coded base address from the datasheet. Indeed, this is exactly what's done in the tm4c header file. At the end of the file, you can find #-defined base addresses of various hardware blocks. And below that you have a bunch #-defined hard-coded pointers to various structure types. For example, the SYSCTL macro defines a pointer to the SYSCTL_Type structure, which is hard-coded to the SYSCTL base address. Symilarly, the GPIOF-high-speed macro defines a pointer to the GPIO_Type structure, which is hard-coded to the GPIO_PORTF_AHB base address. The first step in replacing the way you access the hardware is to change the header file name to tm4c_cmsis.h. You can also remove the lm4f header file from the project. Next, you replace every register access using the new form. For example, to replace the first register access, you take the SYSCTL pointer and append the member access operator. At this point, the IAR editor will help you by displaying all the members of this structure. You choose the appropriate register from the list. To know for sure which one, you might need to consult the datasheet and the SYSCTL_Type structure definition. You proceed in a similar way with all other registers. The GPIO Port-F Data register is actually an array of 255 registers, as you saw a few minutes ago. As you can see, the program compiles cleanly. The last thing left to do is to verify that the LED still blinks as before. This concludes this lesson about the structures in C and the CMSIS standard. In the next lesson, I will talk about pointers to functions, which you will need to understand the startup code and interrupt handlers. If you like this channel, please subscribe to stay tuned. You can also visit state-machine.com/quickstart for the class notes and project file downloads. --- Course web-page: http://www.state-machine.com/quickstart YouTube playlist of the course: http://www.youtube.com/playlist?list=PLPW8O6W-1chwyTzI3BHwBLbGQoPFxPAPM