Embedded C Programing w/ TM4C123G Microcontroller - 2.0 Functions, Macros & Header Files
In our previous section, Embedded C Programing w/ TM4C123G Microcontroller - 1.0 Register Addresses, it was discussed how to blink an LED by interacting directly with register addresses within the TM4C123G Microcontroller. A not very commune practice.
Here, we would like to simplify things by introducing Functions, Macros, and Header Files.
Function
Preliminary Code/*
This program toggles the Green LED ON & OFF by using register addresses.
The other LEDs (Red & Blue) can be configured as well.
All LEDs are high active (a "1" turns ON the LED).
PF1 - red LED
PF2 - blue LED
PF3 - green LED
*/
int main()
{
//Clock Gating Register
*((unsigned int *)0x400FE608U) = 0x20U; //enable clock signal to GPIOF
//GPIODIR Register
*((unsigned int *)0x40025400U) = 0x0EU; //Setting bits 3-1 as outputs
//GPIODEN Register
*((unsigned int *)0x4002551CU) = 0x0EU; //Setting bits 3-1 as digital pins
while (1) {
//*((unsigned int *)0x400253FCU) = 0x02U; //Setting bit 1 to turn Red LED ON
//*((unsigned int *)0x400253FCU) = 0x04U; //Setting bit 2 to turn Blue LED ON
*((unsigned int *)0x400253FCU) = 0x08U; //Setting bit 3 to turn Green LED ON
//Offset: 0x3FC - Bit banding
int counter = 0;
while (counter < 1000000) {
++counter;
}
*((unsigned int *)0x400253FCU) = 0x00U; //Turn OFF LED
counter = 0;
while (counter < 1000000) {
++counter;
}
}
//return 0;
}
/* This program toggles the Green LED ON & OFF by using register addresses.
The other LEDs (Red & Blue) can be configured as well. All LEDs are high active (a "1" turns ON the LED). PF1 - red LED PF2 - blue LED PF3 - green LED */ int main() { //Clock Gating Register *((unsigned int *)0x400FE608U) = 0x20U; //enable clock signal to GPIOF //GPIODIR Register *((unsigned int *)0x40025400U) = 0x0EU; //Setting bits 3-1 as outputs //GPIODEN Register *((unsigned int *)0x4002551CU) = 0x0EU; //Setting bits 3-1 as digital pins while (1) { //*((unsigned int *)0x400253FCU) = 0x02U; //Setting bit 1 to turn Red LED ON //*((unsigned int *)0x400253FCU) = 0x04U; //Setting bit 2 to turn Blue LED ON *((unsigned int *)0x400253FCU) = 0x08U; //Setting bit 3 to turn Green LED ON //Offset: 0x3FC - Bit banding int counter = 0; while (counter < 1000000) { ++counter; } *((unsigned int *)0x400253FCU) = 0x00U; //Turn OFF LED counter = 0; while (counter < 1000000) { ++counter; } } //return 0; }
Example:
#include <stdio.h>
#define Pi 3.14
float CircleArea(float r); // function prototype
int main()
{
float Area, f;
printf("Enters the diameter of the circle: ");
scanf("%f",&f);
Area = CircleArea(f); // function call
printf("Area of the Circle = %f",Area);
return 0;
}
float CircleArea(float d) // function definition
{
float a;
a = Pi*((d*d)/4);
return a; // return statement
}
In addition to main, the piece of block that performance an specific task here is:
float CircleArea(float r) // function definition
{
float a;
a = Pi*(r*r);
return a; // return statement
}
The C compiler recognizes CircleArea(float r) specifically as a function.
void delay() // Function Declaration
Preliminary Code
void delay() {
int counter = 0;
while (counter < 1000000) {
++counter;
}
}
int main()
{
//Clock Gating Register
*((unsigned int *)0x400FE608U) = 0x20U;
//GPIODIR Register
*((unsigned int *)0x40025400U) = 0x0EU;
//GPIODEN Register
*((unsigned int *)0x4002551CU) = 0x0EU;
while (1) {
//*((unsigned int *)0x400253FCU) = 0x02U; //Red LED
//*((unsigned int *)0x400253FCU) = 0x04U; //Blue LED
*((unsigned int *)0x400253FCU) = 0x08U; //Green LED
delay();
*((unsigned int *)0x400253FCU) = 0x00U;
delay();
}
I just replace the two while statements from the previous code:
while (counter < 1000000) {
++counter;
}
with one single function definition called delay:
void delay() {
int counter = 0
while (counter < 1000000) {
++counter;
}
}
Note:
- A Prototype can occur at the top of a C source code file to describe what the function returns and what it takes (return type and parameter list). When this is the case (occurring at the top of the file), the function prototype should be followed by a semi-colon.
- The function prototype is not needed if the user-defined function is defined before the main() function.
MACROS:
A macro is a fragment of code that has been given a name, sort of an abbreviation, which is defined once and then used later.
Syntax
#define CNAME value
Example:
#define GPIO_PORTF_DATA_R *((unsigned int *)0x400253FCU)
Here:
#: Is a preprocessor directive. It acts as a flag or indicator telling the compiler to pay attention to the following text as a special instruction before the regular compilation process.
define: The directive name. In this case the directive name is define. Which means, treat what follows as a MACRO.
#define: The actual preprocessor directive. Telling the compiler to create a macro named GPIO_PORTF_DATA_R with the value stored in this particular address *((unsigned int *)0x400253FCU).
GPIO_PORTF_DATA_R: name of the constant.
*((unsigned int *)0x400253FCU): value of the constant.
Note: semicolon ( ; ) is not needed.
Now, with the same aim of functions the code can be simplify further by using macros.
Typing *((unsigned int *)0x400253FCU) = 0x08U over and over it’s time-consuming and error-prone. Is way effective to handle constant names instead of pointers pointing to different addresses : *((unsigned int *)0x400253FCU)and hexadecimal numbers.
Preliminary Code
/*Macro Definition*/
#define RCGCGPIO *((unsigned int *)0x400FE608U)
#define GPIO_DIR *((unsigned int *)0x40025400U)
#define GPIO_DEN *((unsigned int *)0x4002551CU)
#define GPIO_DATA *((unsigned int *)0x400253FCU)
void delay() {
int counter = 0
while (counter < 1000000) {
++counter;
}
}
int main()
{
RCGCGPIO = 0x20U; //Enable Clock Gating Register
GPIO_DIR = 0x0EU; //set pins 1, 2 & 3 as output.
GPIO_DEN = 0x0EU; //set pins 1, 2 & 3 as digital output.
while (1) {
GPIO_DATA = 0x02U; //Red LED ON
delay();
GPIO_DATA = 0x00U; //Red LED OFF
delay();
}
}
Note: Read C Preprocessor: Macros and Header Files for an overview of macros.
HEADER FILES
Like we just did with preprocessor directive "#define", we can use "#include" as another preprocessor directive to include what is known as "header files".
A header file is a file that contains C function declarations, struct and macro definitions to be shared between several source files. The given name should end with an .h extension.
Microcontrollers vendors already provide C "function" declaration, “struct” and “macro” definitions in a separate file. Therefore, no need to invest in defining the macros by your own. In the case of the Tiva TM4C123G LaunchPad there are two different header files that you can use.
HEADER FILE with MACROS definition
The one from Texas Instrument, which can be find through the TivaWare software suite, includes the “tm4c123gh6pm.h” header file. In this header file, each register is defined with his own name (Macros). For example, the Direction Register of Port F is referred to as GPIO_PORTF_DIR_R and the data register of Port F is referred to as GPIO_PORTF_DATA_R. This file can be found at the installation folder: C:\ti\TivaWare_C_Series-2.2.0.295\inc
With this in mind we just need to remove the macro definitions of our previous program and replace it with a single line statement: #include “LM4F120H5QR.h”.
That is… from this
/*Macro Definition*/ #define RCGCGPIO *((unsigned int *)0x400FE608U) #define GPIO_DIR *((unsigned int *)0x40025400U) #define GPIO_DEN *((unsigned int *)0x4002551CU) #define GPIO_DATA *((unsigned int *)0x400253FCU)
to one single line statement:
#include “LM4F120H5QR.h”
In your IAR Embedded Workbench IDE make one of the include paths be the "inc" folder. And add LM4F120H5QR.h to it.
Example Code (w/ the names as specify in the macro header file):
#include “tm4c123gh6pm.h” // “Header File with macros”
void delay() {
int counter = 0;
while (counter < 1000000) {
++counter;
}
}
int main()
{
SYSCTL_RCGCGPIO_R = 0x20U; //Enable Clock Gating Register
GPIO_PORTF_DIR_R = 0x0EU; //set pins 1, 2 & 3 as output.
GPIO_PORTF_DEN_R = 0x0EU; //set pins 1, 2 & 3 as digital output.
while (1) {
GPIO_DATA = 0x02U; //Red LED ON
delay();
GPIO_DATA = 0x00U; //Red LED OFF
delay();
}
}
HEADER FILE w/ STRUCT definition
On the other hand, the IAR IDE tool provide <TM4C123GH6PM.H> as a header file with "struct" defenition, meaning each port is defined as a pointer to a struct with the registers as the members of the struct, For example, the Direction Register of Port A is referred to as GPIOA → DIR and the Data Register of Port F is referred to as GPIOF → DATA and so on. In the IAR IDE tool this file can be found in the system directory folder: C:\Program Files (x86)\IAR Systems\Embedded Workbench 8.4\arm\inc\TexasInstruments
Example CODE (w/ the name as specify in the struct header file):
#include “TM4C123GH6PM.H” // “Header File with struct”
void delay() {
int counter = 0;
while (counter < 1000000) {
++counter;
}
}
int main()
{
SYSCTL → RCGCGPIO |= 0x20U; //Enable Clock Gating Register
GPIOF → DIR = 0x0E; //set pins 1, 2 & 3 as output.
GPIOF → DEN = 0x0EU; //set pins 1, 2 & 3 as digital output.
while (1) {
GPIOF → DATA = 0x02U; //Red LED ON
delay();
GPIOF → DATA = 0x00U; //Red LED OFF
delay();
}
}
Comments