$24
Objective: To learn the embedded software modular encapsulated structure of coding as well as proper documentation of c-functions. You will take the project framework provided and develop several c-functions to pulse the LED using the LETIMER PWM functionality connecting the LETIMER0 peripheral output to LED0 and to LED1. After initializing the system, the PWM will continue to pulse both LEDs, LED0 and LED1, at the same time without intervention from the most energy consuming peripheral, the CPU.
Key Learning Objectives from this Assignment:
• Debugging: Using ASSERT statements to validate proper programming or operation of the microcontroller. Assertion tests reduce the time, the number of lines of programming code, between functional and non-functional code allowing the programmer to focus on a smaller code segment to debug thus reducing the time to resolve the error.
• Commenting: Proper commenting can help plan your code development and aid in code review thus reducing time for project development and obtaining assistance. You should first develop the comment block before you develop your function, and then update/make changes to the function comment block if necessary as you develop your routine.
• Code Structure, Modularity, and Encapsulation: Software is developed to be portable and reusable. A software driver is low level code that communicates directly with the hardware such as in this assignment, the LETIMER peripheral. The application developer should not need to understand or be required to program hardware specifics. For example, if you have used a printf statement sending a string to a terminal, you did not communicate directly to the hardware, you used an “interface” (input argument) to communicate to a library, “middleware” (printf), which then communicated to a lower level of code, “the driver” (a serial port) to send the string to the terminal. Moving forward in this course, the application code resides in app.c and the driver will be named after the microcontroller peripheral appended with .c such as letimer.c. Once we add an external device such as an I2C temperature and humidity sensor, you will then develop a module that interfaces between the application code and the driver, middleware.
• STRUCT and strings: To pass more than a few arguments to a function, it is easier to define a software STRUCT that contains all the pertinent information. In this manner, only a pointer to the STRUCT is passed via the function’s input argument. In this assignment, in opening a driver, the STRUCT can be analogous to a recipe in cooking.
1
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
The recipe is the STRUCT and all the ingredients specified in the recipe are the elements of the STRUCT.
Note: You will need to download Lab 2 framework from the Canvas Lab 2 folder.
Due: Sunday, September 13th, at 11:59pm
Making change to clarify documentation:
• Italicized text will signify informational details of the assignment
• Standard text will signify activities required to complete the assignment
Lack of best programming practices will result in point deductions starting on this assignment:
• Magic numbers are not to be used
o Magic numbers are use of discrete numbers instead of a constant
▪ count = 32768 * period;
• 32768 is a magic number
▪ count = COUNT_PER_SECOND * period;
• where COUNT_PER SECOND is defined at 32768 in the proper .h header file is the correct implementation
• Warning statements in a build
o When you build the project and look in the problem tab, all warnings from your
code should be resolved. Some warnings can cause your program to fail.
o The below warnings are acceptable. These warnings are stating that in future versions of the library, these routines will no longer be supported. Since we do not have control of these warnings, they will not be deducted from your grade.
Instructions:
1. Download Student_PWM_F20.zip from the Canvas Lab 2 folder.
2. Import Student_PWM_F20.zip into your Simplicity workspace
3. Change the name of the project by adding your two initials in front of the project name PWM_Lab.zip.
a. Ex. For Keith Graham, the name would change to KG_PWM_F20
4. Right click your project and perform a clean project
2
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
5. Before we go into the project work, you will add a resource to help debug your code.
6. ASSERTIONS: Assertions are statements used to test the validity of the code. A programmer places asserts in sections of the code when they want to validate something or perform error checks. If an assertion fails, it can crash the program to alert the programmer, throw an error, jump to a function and wait in an infinite loop.
In our case, Silicon Labs provides a library to handle assertions. Here is a detailed description provided by Silicon Labs:
An assert is an error checking module.
By default, EMLIB library assert usage is not included in order to reduce footprint and processing overhead. Further, EMLIB assert usage is decoupled from ISO C assert handling (NDEBUG usage), to allow a user to use ISO C assert without including EMLIB assert statements.
Below are available defines for controlling EMLIB assert inclusion. The defines are typically defined for a project to be used by the preprocessor.
◦ If DEBUG_EFM is defined, the internal EMLIB library assert handling will be used. This is implemented as a simple while(true) loop. DEBUG_EFM is not defined by default.
◦ If DEBUG_EFM_USER is defined instead, the user must provide their own implementation of the assertEFM() function.
◦ If both DEBUG_EFM and DEBUG_EFM_USER are undefined then all EFM_ASSERT() statements are no operation.
From the description provided, we understand that there are two ways to use asserts, either by using a Silicon Labs function or our own function.
7. To enable the EFM_ASSERT resource, a Symbol must be added to enable them while the code is built and ran. To add a symbol, perform the following:
a. Right click your project, select properties, expand C/C++ Building, click on settings, expand GNU ARM C Complier, and then click on symbols. Click on to add DEBUG_EFM=1. Then click OK, then Apply, and then OK.
i. Do not delete the existing symbols
ii. Do not add spaces
3
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
Symbols are used by the complier to enable or disable certain functionalities or sections of code. Silicon Labs disables asserts by default because they want to lower the processing overhead.
Your project now has DEBUG_EFM defined, which means we will be using the provided assert function.
8. Now we understand what asserts do, but how and why do we use them?
When writing firmware, you want to catch bugs early and quickly, and asserts will help you do just that.
4
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
Here is an example, have a look at this simple code:
The example above will work with no issues, as SOMETHING_I_EXPECT is in fact equal to SOMETHING_I_GOT.
Note: You must add the #include “em_assert.h” to all .c files that are using the
EFM_ASSERT() function. Best practice is to add the “em_assert.h” to the appropriate module’s .h file.
Now, let’s change the example so the assertion fails, by setting SOMETHING_I_GOT to 1. As the example above explains, we will be stuck in a while loop in a function in the em_assert.c file (which you can find under the “emlib” folder in your project):
5
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
That function (assertEFM) helps us get important information, which are the file name and line number where the assertion failed.
You can see why using assertions is extremely helpful, especially as your project grows and you need rigorous error checks at different points.
9. SETTING UP YOUR PERIPHERALS: First, let’s look at the cmu.c file. This .c folder is dedicated to set up the clock tree for the project. With adding a new peripheral to the project, LETIMER0, you will need to configure the clock tree. Establishing the clock tree should be in the cmu_open() function found in the cmu.c file located in your project under /src/Source_Files. Enabling the clock to the peripheral will be done in the open_function of that particular driver/peripheral.
a. For the LETIMER0, we will use the ULFRCO oscillator to enable operation down into the Pearl Gecko Energy Mode 3 level.
i. Normally, you would be required to enable the oscillator for a clock tree, but by specifying to use the ULFRCO for the LETIMER0, there is no requirement to enable the ULFRCO oscillator since the ULFRCO is always enabled in all Energy Modes.
6
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
ii. If one of the other oscillators were required, you would first need to turn on the oscillator using the following emlib routine
1. CMU_OscillatorEnable()
iii. In building the clock tree, the next step is to route the oscillator to the desired clock branch. This routing is done through selecting the proper input of the clock switch mux.
iv. The setting of the clock switch branch mux select lines,
CMU_LFACLKSEL_LFA, for the LETIMER0 is performed using the following emlib routine
1. CMU_ClockSelectSet()
2. Using the Silicon Labs Hardware Abstraction Layer, HAL, document, what clock branch “definition” would you use to specify the LFACLK branch?
7
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
v. You must also enable the Low Energy (LE) clock tree to the Low Energy peripherals
1. CMU_ClockEnable(cmuClock_CORELE, true);
2. This function acts as a global enable to the Low Energy Clock branches
vi. For this assignment, I have commented out the lines of code to initialize the oscillators and clock tree settings for the LETIMER0. You must do the following:
1. Uncomment the lines of code in cmu.c to enable the oscillators and set the clock tree. You can uncomment the lines by selecting the lines of code and while the lines of code are highlighted, click ctrl+/
2. Replace the XXXXX with the correct symbol/enumeration that can be found in the Pearl Gecko HAL documentation for the function call and requirement of the project.
3. HAL documentation can be found at: https://docs.silabs.com/mcu/latest/efm32pg12/
10. READ THIS ENTIRE ASSIGNMENT BEFORE YOU BEGIN THE LAB
11. DO NOT WRITE ANY CODE IN THESE FUNCTIONS UNTIL YOU GET TO SECTIONS OF THIS LAB SPECIFICALLY FOR THESE FUNCTIONS!
a. app_letimer_pwm_open() function
8
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
b. letimer_pwm_open() function
c. app_peripheral_setup() function
12. You will now change the frequency of operation of the Pearl Gecko to 32 MHz
a. HAL documentation can be found at: https://docs.silabs.com/mcu/latest/efm32pg12/
b. The lab framework has set the Pearl Gecko CPU clock frequency by default to 1 MHz. You will find the following Silicon Lab enumeration in brd_config.h that is used to configure the High Frequency Resistor Capacitor Oscillator (HFRCO) to 1 MHz
i. cmuHFRCOFreq_1M0Hz
c. For this assignment, you will need to change the Pearl Gecko CPU frequency to 32MHz.
i. Generally, Silicon Labs creates enumerations (enum) TypeDefs for key parameters that are used to configure the microcontroller. The type def is can be reversed engineered by the following:
1. cmuHFRCOFreq_1M0HZ
a. cmu = CMU_ (name of the peripheral)
b. HFRCOFreq_ (specific name of enum’s TypeDef)
c. TypeDef
2. Concatenating the above, gives you the full enum TypeDef of:
a. CMU_HFRCOFreq_TypeDef
ii. Now, go to the HAL documentation per the link above and type this enumeration TypeDef in the HAL’s documentation search window and hit enter (or return)
9
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
iii. If you have a selection where one is “Deprecated,” do not select the deprecated option. Deprecated indicates that this usage is planned to become obsoleted sometime in the future
1. In the above example, click on CMU- v5.9
10
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
iv. You will be taken to the CMU module of the HAL documentation. Perform a ctrl+f to open a search window in your browser and search this webpage for your enum TypeDef, CMU_HFRCOFreq_TypeDef
v. You should now see the list of different frequencies that can be defined for the HFRCO oscillastor
1. Copy the enumeration for 32 MHz and replace the 1MHz enumeration to define the HFRCO frequency in brd_config.h
a. #define MCU_HFXO_FREQ “32MHz enum”
2. Now, the next time you compile, your project will configure the HFRCO to 32 MHz instead of 1 MHz
13. Now, open up the letimer.h file. Complete the following define statement. In the cmu.c file, you set up the ULFRCO oscillator to the LETIMER0 clock tree. Replace XX with the number of HZ when using the ULFRCO oscillator.
i. #define LETIMER_HZ XX
ii. You can locate this information in the Pearl Gecko Reference Manual
14. The letimer.c/.h files are designed to be modular and encapsulated. The function xxx_open.c are meant to be drivers to open and initialize the letimer peripheral as a pwm peripheral and for routing its output signals to gpio pins.
a. SECTION 14 DESCRIBES THE GENERAL FLOW OF DEVELOPING A PERIPHERAL DRIVER. DO NOT ENTER ANY CODE UNTIL YOU GET TO THE NEXT SECTION. THIS SECTION IS FOR REFERENCE ONLY.
11
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
b. With the Pearl Gecko being a low energy microcontroller, all clocks to a peripheral upon reset are disabled.
c. The first operation in each xxx_open.c must enable the clock to the peripheral. Without a clock to the peripheral, you cannot write to the peripheral. You can use the following instruction to enable the clock to the peripheral.
i. CMU_ClockEnable(You can find the enumeration in the HAL doc, true);
ii. Let’s take a look at how to find the enumeration to be used to enable the clock for the peripheral. Open up the HAL documentation per the earlier link. Now search for CMU_ClockEnable.
iii. You will want to click on the CMU-V5.9 selection below because you have been search for a Clock Management Unit, CMU_ClockEnable(), function
12
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
iv. Once in the clock management unit, do a search on the web page for CMU_ClockEnable() which will take you to the function’s Function
Prototype and click on it
v. You will now be taken to the Doxygen comment information for the CMU_ClockEnable() function. The first input argument, CMU_Clock_TypeDef defines what clock element to enable. We will need to click on the hyperlink CMU_Clock_TypeDef to obtain the list of clock elements
13
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
vi. Scroll through the enumerations defined within CMU_Clock_TypeDef until you find one that would be appropriate to enable the LETIMER0. As an example, cmuClock_ADC0 would enable the clock to the Pearl Gecko’s
ADC0 peripheral
1. To enable the clock to the ADC0 peripheral, you would use
a. CMU_ClockEnable(cmuClock_ADC0, true);
2. For this assignment, you will need to locate the enumeration to enable the clock to the LETIMER0 peripheral
a. What enumeration enables the clock to LETIMER0?
d. Next, you must initialize the peripheral using the data being passed to it via the STRUCT pointer argument to the xxx_open.c functions.
14
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
i. These STRUCTs can be found in the letimer.h files
e. Since these opens are dedicated to PWM functionality, the structures only includes variables that need to be changed to generically implement the PWM function. The variables that are constantly set for PWM operation are not included in the input structure argument and should be set within the letimer_pwm_open.c function to enable proper pwm operation.
i. Information on how to implement the initialization of each peripheral can be found in the Pearl Gecko Reference manual
1. LETIMER chapter
ii. Let’s take a look at the flow of how a peripheral is enabled in the screen print below
1. Do not implement the letimer_pwm_open() function at this time. Wait until you reach the section labled as letimer_pwm_open() function to develop the routine.
iii. Most of the Silicon Labs’ peripherals have a TypeDef structure that is used to initialize the peripheral. You want to define a local version of this TypeDef to store the values that will be used to initial the peripheral per the input arguments or application specifics such as in this case a free running PWM. Please refer to the GREEN ARROW.
iv. The RED ARROWS are comments that will help you understand the flow of initializing the peripheral. You should input the code to implement the statement after the comment.
15
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
v. The BLUE ARROW points to where the local TypeDef variable is initialized by the application specifics of the assignment and the input arguments from the input argument structure app_letimer_struct.
vi. Other than the LETIMER_INIT() function, what else must be programmed or initialized in the LETIMER peripheral before it is enabled?
1. For example, using the HAL documentation, what library functions would you use to store the values into the COMP0 and COMP1 registers?
f. Let’s review STRUCTs and how to access information on them and utilize them in Simplicitiy
i. STRUCTs are very useful in declaring and using variables that are associated with each other such as all the variables used to initialize a microcontroller peripheral. With the variables being grouped together in memory, a single pointer can be used to send the information contained in the STRUCT as a function argument.
1. A struct in the C programming language (and many derivatives) is
a composite data type (or record) declaration that defines a physically grouped list of variables under one name in a block of memory, allowing the different variables to be accessed via a single pointer or by the struct declared name which returns the same address. (Wikipedia)
ii. For any variable, you can go directly to where it is declared even if it is in a different file by selecting the variable and pressing Function F3. To learn more about the LETIMER_Init_TypeDef STRUCT, select LETIMER_Init_TypeDef indicated by the GREEN ARROW above and press Function F3. Simplicity will open where LETIMER_Init_TypeDef is declared in the file em_letimer.h. You may need to scroll up to see the complete declaration.
iii. You can see that in the example code below, a STRUCT is being accessed in two methods:
1. letimer_pwm_values.debugRun = app_letimer_struct->xxx
16
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
iv. To access STRUCTs within the space that they are defined such as a local STRUCT in this example, all the STRUCT variables can be accessed by STRUCT name, period, STRUCT element name.
1. letimer_pwm_values.debugRun
v. A nice feature in Simplicity is for all local STRUCTs, after you complete the STRUCT name and period, if you wait a second or two, it will list all the possible variables of the STRUCT to select to complete defining the STRUCT element that you would like to access
vi. The second structure being accessed in our example is a STRUCT that is being passed to the function via a pointer. With this STRUCT, you will use the second method of accessing a STRUCT due to the use of being passed a pointer
1. STRUCT name, ->, STRUCT element name
a. STRUCT name is the base pointer to the STRUCT
b. -> indicates add STRUCT address offset of the STRUCT element
vii. Similar to accessing a local STRUCT, if you complete the STRUCT name plus -> and wait a second or two, Simplicity will bring up all the elements of the STRUCT for you to select
g. The LETIMER xxx_open.c function should not enable the timer. You will want to enable, turn-on, the LETIMER after all modules and peripherals have been initialized in the app.c function app_peripheral_setup(void).
17
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
i. What function in the HAL documentation can be used to enable the LETIMER0 before your program enters the while(1) loop in main.c?
1. What would be the c-code to enable LETIMER0 if writing directly to the LETIMER CMD register?
ii. Check out the code in app_peripheral_setup() at the end of the function. Why is the call to start the LETIMER not what is defined in the HAL documentation?
1. For the code to be modular and encapsulated, you want all accesses to the peripheral registers to be done by your driver .c code, letimer.c
2. What have you been using that models this programming concept?
a. The em_lib routines in the HAL documentation
b. You will not find another module such as the ADC module manipulate a register in another peripheral like the
LETIMER.
3. What is the em_lib function to enable or start the letimer peripheral?
4. With the em_lib function that you found in the HAL documentation to enable or start the LETIMER, complete the letimer_start() function found in letimer.c
15. app_letimer_pwm_open() function:
a. The project is structured to have a driver, letimer_pwm_open(), which will configure the LETIMER0 peripheral for PWM operation.
b. For this assignment, the PWM function is to have a period of 2.7 seconds with an active time, on-time of the LED, of 0.15 seconds. I have included the relevant required application information in the app.h file for this lab. Both LED0 and LED1 will turn-on and turn-off in unison.
c. The application requirements for the letimer_pwm_open() function can be found by looking at the function prototype of the letimer_pwm_open() found in letimer.h
i. void letimer_pwm_open(LETIMER_TypeDef *letimer, APP_LETIMER_PWM_TypeDef *app_letimer_struct);
d. With the main objective of this function to send the application specific requirements to the letimer_pwm_open() function, the first line of code must be the declaration of a local APP_LETIMER_PWM_TypeDef STRUCT
18
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
i. APP_LETIMER_PWM_TypeDef struct name of your choice;
e. With a local STRUCT now declared, the next step is to configure the value of each
“struct of your choice” elements that will be sent to letimer_pwm_open() to satisfy the application requirements
i. “struct name of your choice”.element1 = xxx;
ii. “struct name of your choice”.element2 = xxx;
iii. “struct name of your choice”.elementn = xxx;
f. Please note that in the process of configuring / initializing the LETIMER0 peripheral, you will be using two different STRUCTs. The first STRUCT is the APP_LETIMER_PWM_TypeDef that you just initialized. This STRUCT contains additional information to complete the configuration of the LETIMER0 peripheral than what is included in the LETIMER_Init_TypeDef. For example, the period and active_period elements in APP_LETIMER_PWM_TypeDef are not required for the LETIMER_Init_TypeDef, but are required to initialize the values of the LETIMER0 COMP0 and COMP1 registers.
g. With the STRUCT to initialize your driver now configured, you have one of the input arguments to letimer_pwm_open() ready
h. The next, first, argument in letimer_pwm_open() is LETIMER_TypeDef *letimer. If you select LETIMER_TypeDef and press Function F3, you will see its declaration
i. The LETIMER_TypeDef STRUCT defines the registers/elements for each of the registers in the LETIMER peripheral which also provides the offset address to access these registers from the base address of the STRUCT
j. If you search for the LETIMER0 declaration, you will see the following:
i. #define LETIMER0 ((LETIMER_TypeDef *) LETIMER0_BASE) /**< LETIMER0 base pointer */
19
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
ii. The comment of this define statement clarifies that LETIMER0 is the base address that will be used to access this STRUCT which implies to inform the driver of the LETIMER peripheral, all you need to do is use the LETIMER0 defined constant for the argument LETIMER_TypeDef *letimer
k. Now, you have both arguments to open the letimer pwm driver. The last line of code for this routine should be:
i. letimer_pwm_open(LETIMER0, “struct name of your choice”);
16. letimer_pwm_open() function:
a. The imported project, PWM_F2020.zip, provides the complete framework required for this project where all the functions that are required to implement the project have their function prototypes defined in app.h and letimer.h.
b. As mentioned above, what is the first thing that should be done in a driver?
i. Enable the clock to the LETIMER peripheral
ii. Previously in the cmu_open() function, you routed the ULFRCO oscillator onto the LFA clock branch through the LFACLK clock switch, highlighted in dark gray / black below. The ULFRCO clock still is not yet at the LETIMER0 peripheral due to the clock gate in the below diagram, highlighted in red. The clock gate acts as an AND gate.
iii. The clock gate, “AND logic,” has two inputs. One input is the schematic signal CMU_LFACLKEN0.LETIMER0, highlighted in blue, and the other signal is now the ULFRCO clock which has been routed to the “AND gate.”
Per the AND Gate Truth Table, when the schematic signal
20
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
CMU_LFACLKEN0.LETIMER0 is deasserted, 0, the output of the clock gate is always 0. When the schematic signal CMU_LFACLKEN0.LETIMER0 is asserted, 1, the output of the clock gate is equal to the ULFRCO clock signal. To pass the ULFRCO clock to the LETIMER0 peripheral, you will program the schematic signal CMU_LFACLKEN0.LETIMER0 to be asserted, set to 1.
iv. With the proper function input arguments, the CMU_ClockEnable() will either assert, set to 1, or deassert, clear to 0, the CMU_LFACLKEN0.LETIMER0 signal. Setting the function argument Enable to true will assert, set to 1, this control signal while Enable set to false will deassert, clear to 0 CMU_LFACLKEN0.LETIMER0.
v. With the concept that the letimer_pwm_open() function is a generic driver that could support any of the LETIMERs found in the Pearl Gecko or similar micro-controller, you will need to use an if statement to properly select the correct input for the CMU_ClockEnable() function. You will use the LETIMER_TypeDef *letimer argument found in the letimer_pwm_open() function call.
1. For example if (letimer == LETIMER0), you will use the CMU_ClockEnable() that enables the LETIMER0 clock
2. Since there is no LETIMER1 in the Pearl Gecko, do not add an IF statement for LETIMER1.
vi. CMU_ClockEnable(You can find the enumeration in the HAL doc, true);
c. As a low energy micro-controller, all clocks to the peripheral and the peripheral’s registers are disabled by default. Without proper clock tree configuration and enabling, data written to a peripheral register will not occur. The instruction will not “crash/terminate” the program or notify you that the write did not complete correctly. To test the clock tree configuration and that the clock is enabled, add the following sequence of code with an ASSERTION test. The code tries to start the LETIMER by writing a command bit to the CMD register and verifies whether the LETIMER has started by reading the RUNNING status bit from the STATUS register. If successful, the code then stops the LETIMER by writing a bit to the CMD register to STOP. After stopping the LETIMER, the code then must re-initialize the CNT register to the value 0 so that upon enabling after initialization,
21
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
it will quickly load the top value from COMP0 instead of the default value of 0xffff.
d. If the clock is not enabled, the program will remain stuck at the while (letimer-> SYNCBUSY) instruction or the ASSERTION test will fail and the code will jump to the assertEFM()’s while(1) loop.
i. Indicating one of the following:
1. The peripheral clock is not enabled
2. The peripheral clock tree is not properly configured
3. Or, the input argument pointer for letimer is incorrect
ii. You will learn more about SYNCBUSY in a future lab assignment
e. By passing pointers to LETIMER to open via its data structure, LETIMER_TypeDef *letimer, your driver/open function can be universal, supporting all peripherals of the same kind.
i. With the base address of the data structure, letimer, all of its registers can be accessed via using the -> assignment such as letimer->IF will access the LETIMER Interrupt Flag register
f. After the LETIMER clock has been enabled, the next step is to use the configuration information sent to the letimer_pwm_open() function through the letimer_pwm_open argument APP_LETIMER_PWM_TypeDef *app_letimer_struct to initialize the LETIMER through the LETIMER_Init() function.
g. The LETIMER_Init_TypeDef STRUCT used by the LETIMER_Init() function is not the same as the APP_LETIMER_PWM_TypeDef. To initialize / assign values to the LETIMER_Init_TypeDef STRUCT elements, you will be using values from the input argument STRUCT, app_letimer_struct, as well as direct assignments that will make this LETIMER0 peripheral implement a PWM function.
22
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
h. The LETIMER is on a different oscillator / frequency compared to the main processor clock, so the LETIMER’s peripheral registers will be asynchronous to the processor’s clock. To synchronize these clocks, these registers go through a two-stage synchronization circuit.
i. If you go into the LETIMER_Init() library function, if LETIMER_Init_TypeDef input argument enables the LETIMER, the CMD register of the LETIMER peripheral will be written to.
23
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
j. Per the LETIMER peripheral SYNCBUSY register, the CMD register can be tested for synchronization to be completed
k. To validate whether the CMD register is in process of being synchronized and stall until the synchronization has completed, use a while() statement that continues to check the SYNCBUSY register until it returns false, 0, indicating no synchronization is in process.
i. while(letimer->SYNCBUSY);
ii. Add this check and stall immediate following the LETIMER_Init() function call
l. After you have initialized the LETIMER, you will then need to calculate the values to store in the LETIMER’s COMP0 and COMP1 registers.
m. Similar to accessing the peripheral registers using a base pointer of a data structure, the data configuration information is being passed to letimer_pwm_open() via a structure, APP_LETIMER_PWM_TypeDef *app_letimer_struct. Any variable defined in this structure can be accessed by using the -> assignment. For example, the period variable in the structure app_letimer_struct defined by APP_LETIMER_PWM_TypeDef can be accessed like the example below:
i. period_cnt = app_letimer_struct->period * LETIMER_HZ;
24
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
ii. The above c-line of code will calculate the LETIMER counts required for the LETIMER period based on the defined number, LETIMER_HZ, of HZ for the LETIMER clock source
n. With the COMP0 and COMP1 registers now defined, you must complete the LETIMER driver open by routing the LETIMER0 outputs to the output pins of the Pearl Gecko to blink the LED.
o. LED0 is connected to gpioPortF, pin 4 per Lab 1. To route the output of LETIMER0 peripheral to the demultiplexer output which allows the signal to the proper Pearl Gecko’s Output pin, the select lines, ROUTELOC0, of the demultiplexer must be configured. Once the select lines have been configured, then you must enable the output of the demultiplexer, ROUTEPEN, so the LETIMER0 output will now drive directly the Pearl Gecko output.
i. You should assign the bit in the register with the value sent to the letimer_pwm_open() function via the app_letimer_struct
ii. Ensure that you are writing the correct bit location for OUT0 and OUT1 in the ROUTEPEN register
p. To route the output of OUT0 of the LETIMER0 to the Pearl Gecko GPIO pin, the ROUTELOC0 register must be set with the correct routing selection value. This value can be found in the Pearl Gecko datasheet.
25
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
q. The number after the peripheral output, LETIM0_OUT0, is the routing location, #28, to write to the correct bit field in the LETIMER0->ROUTELOC0 register
r. Particular registers must be configured in a particular way.
17. app_peripheral_setup() function
a. In the app.c file, you must add the arguments to the function calls within the app_peripheral_setup.c function. You will need to uncomment these lines of code. You can use ctrl+/ as a short cut to uncomment the lines of code at one time. These functions will be used to set up the structures that will be used by the peripheral’s open driver functions.
i. app_letimer_pwm_open(, );
ii. The arguments for these functions can be found in their function prototype definitions located in their associated .h files.
26
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
18. The above app_letimer_pwm_open() function calls the LETIMER open functions. This function call should set the structure defined in the letimer.h file with the desired values to enable proper PWM operation.
19. Each function in app.c, cmu.c, and gpio.c should have a doxygen comment block before the function that fully describes what the function performs, the input arguments, and outputs. Below is an example from a Silicon Labs embedded library function.
20. I have provided the Doxygen block framework in the app.c template that you had downloaded to begin this project.
/*************************************************************//**
• @brief
•
•
• @details
•
•
• @note
*
*
• @param[in]
•
•
• @param[in]
•
*****************************************************************/
21. In addition to each comment block, the file must have a doxygen module block that provides information on the module file name, author, date, and module brief. The brief provides a summary of what the code in its .c module performs. For example, in
27
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
cmu.c, the brief could be that the cmu.c module is responsible in enabling all oscillators and routing the clock tree for the application.
/**
◦ @file
◦ @author
◦ @date
◦ @brief
◦
*/
a. Please note that the @file must refer to the exact name of the .c file that this Doxygen file document header is located.
22. You will need to add and complete the doxygen file and function comment blocks in cmu.c and gpio.c modules as well.
23. For the letimer.c module, you will need to update the doxygen file comment block with you as the author.
24. With the doxygen comment fields completed, its time to prepare and generate the doxygen report.
• Doxygen (/ˈdɒksidʒən/ DOK-see-jən)[2] is a documentation generator, a tool for writing software reference documentation. The documentation is written within code, and is thus relatively easy to keep up to date. Doxygen can cross reference documentation and code, so that the reader of a document can easily refer to the actual code. (Wikipedia)
• Doxygen is free software, released under the terms of the GNU General Public License version 2 (GPLv2).
25. You can download Doxygen by going to git hub at the link below and selecting the proper version based on your operating system
a. http://www.doxygen.nl/
26. Below is a link to a short doxygen tutorial:
a. http://fnch.users.sourceforge.net/doxygen_c.html
27. Doxygen looks for commands within comment statements identified by the @ character such as:
@brief
@author
@date
@detail
@note
@param[in]
28
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
@return
@par (new paragraph)
28. Once you have installed doxygen, open doxygen to initiate your doxygen comment report.
29. In the wizard step 1, browse to your workspace and then project
30. In wizard step 2, add the following:
a. Type in your Project name, Project synopsis, and Version
b. For source code directory, browse into your Simplicity Workspace, Project, and select your src folder
31. Continuing wizard step 2, add the following:
a. Make sure that Scan recursively is checked to enable the scan of both your header and source files
b. Also, browse to a folder outside of your workspace to store the output of this Doxygen document
c. Then, click on Next to proceed to Mode menu
29
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
32. In the mode menu, perform the following:
a. Select Documented entities only
b. And, Optimize for C or PHP output
c. Then click on Next to go to the Output menu
33. In the output menu, perform/verify the following options:
◦ Check HTML
◦ With navigation panel
◦ Check with search function
◦ Then click on next to proceed to the Diagrams menu
30
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
34. In the Output menu, select the following:
a. Use built-in class diagram generator
b. Then, click on Next to move to the Run menu
35. In the Run menu,
a. Click on Run doxygen, and you will see the progress as your documentation is being auto generated
b. Once it is finished, to view your documentation in your browser, click on Show HTML output
31
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
36. After clicking Show HTML output, it should open a web browser. Click on files to find your .c files (letimer.c and app.c)
37. Doxygen debug hints:
a. If your module such as app.c does not show up in your doxygen report
i. The file name in your doxygen module comment block must be identical to the name of the file. For instance for the module letimer.c
1. @file letimer.c
b. If you comments are not showing up in your doxygen report
32
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
i. Doxygen uses the /** to indicate the start of a doxygen comment statement. If you use /*, the doxygen report generator will skip that comment assuming it is a standard and not a doxgen comment block
38. With your doxygen report generated, to open it up after you have closed the doxygen wizard, go into the generated html folder and clock on index.html. Opening this file will open your browser and doxygen report.
39. Now, let’s go back to your project and run your project using the Energy Profiler. Make sure that you toggle the switch on the Pearl Gecko to reset the current monitor to get the best measured results. A correctly developed program will have an Energy Profiler like the screen shot below:
40. If your code never failed an EFM_ASSERT() test, go back to your code and try the following:
a. Add the following line of code immediately before you call letimer_pwm_open() in your app_letimer_pwm_open() function
i. EFM_ASSERT(false);
b. Run your code in the debugger
i. Does your LED0 blink? I suspect no
ii. Pause your program in the debugger. It should have failed the
EFM_ASSERT() test that you added to this function. You can determine which EFM_ASSERT() test failed by using the information in the upper right hand screen as previously discussed in this lab or you can use the upper left window.
33
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
c. In the upper left hand window of your screen, you will see a list of all the functions that were called to get to the assertEFM() function
i. main.c called app_peripheral_setup()
ii. app_peripheral_setup() called apps_letimer_pwm_open()
iii. apps_letimer_pwm_open() called assertEFM()
d. Click on app_letimer_pwm_open() indicated by the blue arrow above
e. If you click on the routine that called assertEFM(), the function immediate after assertEFM(), it will open the file that contains the EFM_ASSERT() function call. The highlighted c-line of code will be the actual EFM_ASSERT() statement or as in this case, the c-line of code immediately after the EFM_ASSERT() call
f. Before you forget, delete the EFM_ASSERT(false) line of code that you added in app_letimer_pwm_open() so that your program does not fail this EFM_ASSERT() and you can answer the questions for the Lab 2 worksheet.
Deliverables:
1. Each person exports their project as an archived filed. Upload the .zip file into the Lab 2 exported Project canvas assignment
2. Each person exports their doxygen report as an archived, .zip, file into their lab 2 exported Project canvas assignment
34
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
3. Each person to provide their answers via Canvas/quiz/Lab2 Worksheet
Questions:
Please complete the Lab 2 worksheet to complete this assignment.
Point breakdown:
• Lab 2 has a total of 35 points
• Exported project will be graded on: o Functionality
◦ Proper period of operation
▪ Can be found in app.h
◦ Proper active period of operation
▪ Can be found in app.h
o Meeting Energy / Current expectations
◦ Energy Profiler results
o Proper commenting of functions o No use of magic numbers
o Best coding practices
• Partial credit will be given on code that is not completely functional
Possible point deductions
• Use of Magic numbers
o Take this deduction only once for this assignment (-1 pt)
• Perform a Clean Build and then build the project
o Point deduction for each warning statement which is not related to a Silicon
Labs emlib routine (-1 pt / warning)
o These warnings do not warrant any point deductions
Late Penalty deduction:
• Exported program
o Due day + 1 day
o 1 day late to 3 days late
o 3 days late to 5 days late
max score is 30 pts
max score is 25 pts
max score is 15 pts
35
READ ENTIRE LAB ASSIGNMENT BEFORE YOU BEGIN
o After 5 days late max score is 0 pts