In this video, we're going to discuss how to simplify a programmers access to register memory. Microcontrollers are made up of many different types of memory. Code and data memory allows us to execute a program and manipulate data. However, a microcontroller has other memory that allows us to interact with special functional units called peripherals. Microcontrollers have a number of peripherals and each have special functionality. To interact with these peripherals, all we need to know is the address of the register memory, the size of the register, and the bit field settings for each register. This information is provided in the family technical reference manual. However, once we write software that interacts with a peripheral address, we are making our code platform dependent and potentially hard to read given an arbitrary address. We can use a special type of file called the register definition file that allow us to make our code more portable and readable for an embedded software engineer. Microcontrollers have numerous peripheral devices. Some are internal CPU, some are external CPU, some can even be private. By adding up all the registers that may make up these spaces, you could be left with an enormous number of addresses and bit fields that you need to track functionality for. We've typically used pointer variables to directly interact with memory. If we had to create a pointer variable for every peripheral register that we had to interact with, then we'd be wasting tons of data memory to hold those pointer variables for each register. In addition, these peripheral register addresses cause our program to be dependent on one particular platform or chip within a chipset family. To mitigate these concerns, we're going to introduce a file that provides extraction to the hardware interface called the register definition file. For every chipset platform that gets produced, hardware and software engineers work together to create a hardware interface file, known as a registered definition file. This file provides software mechanisms to track all of the platform registers through meaningful name references. Sometimes these register definition files are very simple and just provide a list of addresses that map to names. This may or may not include information on specific bit fields for each individual register. Other times, these files have very descriptive register mappings that provide software that actually allows you to interface with the memory directly. We will go through some examples of what these files might contain. But first, let's understand two concepts that will allow us to interact with memory without creating any data storage. The ability to directly dereference a memory location with a specific address or a structure overlay. Starting with directly dereferencing memory, let us say we have the timer A0 register example from the NSP 432. This register has many bit fields and is located at the address, 0x40000000. We can create a pointer to this location, dereference it, and assign a value. However, we can actually write software that can directly dereference a memory location without creating an intermediate variable. To do this we take a hardware integer address of the peripheral, we then cast that to be a pointer. Once it's casted, we can dereference it and assign an appropriate size of data. These operations would be equivalent to creating a pointer variable, initializing it and assigning data to that memory. However, with directly dereferencing memory space, we save on data memory storage. A disadvantage of this code is that it's pretty hard to read and maintain. We can utilize preprocessor macros to fix this. Instead of using a hard coded address, we will define an address as a preprocessor macro, TA0CTL. We can simply use a #define to this address with the name TA0CTL and allow the preprocessor to do the search and replace for us. However this interface is still hard to understand. We can run a macro to address the memory location and provide the address through another macro. Here we have three sets of macro functions that can read 8, 16 and 32-bit memory locations. Each are macro functions where you pass in an address as the x variable, and this will read an associated number of bytes from that x location. You simply pass in the address you want to read and the appropriate direct dereference function called occurs. Now, you can assign data to the macro TA0CTL and it will write to that memory location. This is because the preprocessor will substitute this macro function for the actual C code before compile time. We can now write the register interface file to interface with all the Timer_A0 Registers using these macros, and none of these take up data memory. By using a general macro name within a register definition file as an interface, we can actually create some portability between higher level software and the lower level firmware. Say we had to have different chips within the family. They have the same peripherals but they are located at different addresses. We can actually provide a compile time switch based on our platform to switch which header file or register definition file is included. Thus, we can use a compile time switch to dynamically switch the correct register addresses for a given platform. One small addition to this, is we need to add the volatile keyword to the register interface. This type qualifier tells the compiler to not optimize this memory interaction. Meaning, it will enforce a direct or write to a location anytime this keyword is used with a variable or an address. This is important, because we want our peripheral devices to be configured immediately upon writing to them and not when the compiler can optimize this interaction. We will talk about the volatile keyword later in the course. The other mechanism we will be interfacing with peripherals is referred to as a structure overlay. This requires us to define structures for each peripheral region, size each member of the structure to map to the corresponding registers, and align these members so that they have the right offsets into the register's 4KB address space. This method only requires us to track a single address for a peripheral instead of every register address inside the peripheral region. Given the base address of the peripheral region, we can index into this memory space to access other registers. Similarly, with a structure, once we have a pointer to a structure we can dereference many of the fields, tracking the structure address in an offset into the structure. The structure overlay method requires an exact replica of the physical memory defined in your structure definition. Order and size must be preserved. If addresses are skipped, you need to add in reserved bytes. If you have registers that are read-only, you can add the Const! keyword. Here we have the structure defined with the CTL register first, followed by the the capture the compare control registers, CCTL. The time array counter register comes next, then a capture compare register CCR. The expansion register, then there is a large gap of a reserved bytes because these addresses are not used. Finally we have the interrupt vector register. Just like directly dereferencing an address, we can actually create a structure pointer to this structure definition without allocating any data memory. Using the structure pointer derefernce operator, the arrow can directly derefernce the peripheral region and offset to the correct memory spot. In this example we use a preprocessor macro to track the base address. We then cast that address to a structure pointer. We assign the same control register or the CTL register the hex value 0202. You do this by using the structure dereference error. There are many ways to create a register definition file. We can just truck addresses, we can create direct address dereference macros, or we can create structure overlays. These methods allow us to have a very readable and maintainable platform interface without extra computation or memory storage overhead on a microcontroller.