This video presentation is the fifth installment of the Essentials of MQX RTOS Application Development training course. In this session, you will be introduced to MQX driver architecture, how drivers are initialized and used, and walk through two very popular drivers - serial, and light-weight GPIO.
This training was created by Embedded Access Inc., a Freescale sponsored training provider and proven partner.
|Session 5 Course Line||Lab Outline|
First, watch the video for Session 5: Introduction to Drivers.
Then, follow through with the interactive lab assignment below.
SESSION 5: LAB ASSIGNMENT
The lightweight GPIO (LWGPIO) driver is used for monitoring inputs and controlling inputs. Though the concept is quite straight forward there are a number of structures and functions you need to use with this driver. In part this is to keep your code easily portable should you change processors and in part because of the complexity of modern processors such as Kinetis.
The objective of this lab is to implement the switch and LED functionality.This objective will be accomplished by:
- Adding code to the Input Task to poll inputs from switches
- On a change of state for one of the switches, sending a message to the health task who passes it on to the Display Task
- Adding code to the Display task to set the orange LED to reflect the state of sw1 and the yellow LED to reflect the state of sw2
- Toggling the state of the green LED every second
New functions/ structures you will use:
LWGPIO_STRUCT, LWGPIO_PIN_ID, lwgpio_init, lwgpio_set_functionality, lwgpio_set_attribute, lwgpio_get_value, lwgpio_set_value, lwgpio_toggle_value
INITIALIZING THE INPUTS
- We will be focusing on messages related to GPIO for the time being so to avoid confusing our results with extra messages we'll disable the messages from the Accel Task and the Temp Task. You can either comment out the sending of messages from these two tasks or after they print out a message to say that they've started permanently block the task.
- The job of the Input task is to monitor the inputs but so far the inputs have been simulated by periodically sending a place holder message. This can now be replaced by only sending a message when the state of either switch 1 or switch 2 on the K70 tower card changes state. If you recall, in the lab for another session we defined all of the message types in main.h in an enum. We added Input_Message as the message type from the Input Task but now that we'll have two types of messages, one for switch 1 (sw1) and one for switch 2 (sw2). Delete Input_Message from the list and add SW1_Message and SW2_Message.
- In the Input Task we'll need to declare a few variables of different structure types associated with LWGIO. At the top of the function where the variable declaration is done add variables sw1 and sw2 both of type LWGPIO_STRUCT. We also need to know the value of sw1 and sw2 so declare variables sw1_value and sw2_value of type LWGPIO_VALUE. Finally, to retain the previous value of each switch we'll need two more variables of type LWGPIO_VALUE called sw1_last_value and sw2_last_value.
- In order to use an I/O on the processor it has to first be initialized to define how you want to use it. In the initialization section of the Input_Task use the _lwgpio_init() function to initialize the input pin connected to sw1 and again to initialize the input pin associated with sw2. The ID for each pin is defined in the BSP for the card you are using, in this case twrk70f120m.h which can be found in the BSP_Files folder of the BSP project. Since these we are using these pins as inputs we can use an initialized value of LWGPIO_VALUE_NOCHANGE.
- We also have to set the functionality for each pin and _lwgpio_init() doesn't set the actual pin mux register, meaning that we haven't defined that the associated pins on the processor should actually be used as IO. Use the _lwgpio_set_functionality () function to define the associated pins as inputs. The defines for the different functionality options for the IO pins are defined in the same BSP file (towrk70f120m.h), ie BSP_SW1_MUX_GPIO is used to set pin D0 the internal signal for sw1 and BSP_SW1_MUX_GPIO associates pin E26 with the internal signal for sw2.
- Since the circuits on the tower board for the switches don't have pull up resistors you need to enable the internal pull up resistors using the lwgpio_set_attribute () function.
MONITORING THE SWITCHES
- First thing to do in the endless loop of the Input Task is to read the value of the switches using the _lwgpio_get_value() function and store the result in sw1_value and sw2_value.
- If the read value is not the same as the previous value, it must have changed so this is when we send a message to the Health Task. Copy the code we had before for sending a message. Update the message type (either SW1_MESSAGE or SW2_MESSAGE) and instead of sending data of 0 like we did before we can send the switches value (sw1_value or sw2_value). We are detecting switch state changes by comparing the current value to the last value so now that we've detected a change the current value should be copied to the last value.
- If you haven't thought about this already we could get a false change of state detection on power up if sw1_last_value and sw2_last_value are not initialized. Since the read value will be a 1 when the switch is not pressed it would be a good idea to pre-initialize the last value to a 1 (LWGPIO_VALUE_HIGH).
- Change the time delay in the endless loop to 100 ms to make sure we catch every press of a switch.
UPDATING THE DISPLAY
- Since we are now sending actual data in our messages it would be good to have the Display Task print out the switch state when an update is received. To do this update the printf in the Display Task to show this parameter as well which is being passed in the data field of our message.
- Compile and run the code.
INITIALIZE THE LEDS
- As was done in the Input Task we will need to initialize the GPIOs that control the LEDs, but of course they will now be configured as outputs.
- In the Display Task declare a variable of type LWGPIO_STRUCT for each LED. Since we have 4 LEDs that we want to control it may be more convenient to use an array like this "LWGPIO_STRUCT leds;"
- We also need to keep track of the IDs (eg BSP_LED1) for each LED which are defined in the twrk70f120m.h file. If you using an array for the LWGPIO structures it would make sense to use an array for these as well.
- In the initialization section of Display Task we need to initialize the IO pins associated with the LEDs in a similar way to how it was done for the switches. However we now want to set the direction as being an output (LWGPIO_DIR_OUTPUT) and the initial value should be high with a value of high (LWGPIO_VALUE_HIGH) because we want the output of these pins to reflect the value of the corresponding switches and those are defaulted to high (remember the pull ups?).
- As was also done with the IO for the switches we need to set the functionality for the corresponding pins so they are configured as IO. You'll need the define from the BSP header file again (eg: BSP_LED1_MUX_GPIO)
- Since there are 4 LEDs to initialize you could consider using a for loop for the initialization to keep things cleaner.
- Compile and run the code.
- Our last requirement is to toggle the state of the green LED (LED2) every second. This can be accomplished with a 1000ms timeout to the msgq_receive()function in the Display Task. This function will return NULL when the timeout expires, otherwise it returns a pointer to the received message. Toggling an output can be done by keeping track of the current setting and using the lwgpio_set_value() function or more simply by using the lwgpio_toggle_value() function.
- Compile and run the code to verify that the switch processing still works and that the green LED is toggling.
Need more help? The full source code for this lab can be found in the 'Lab Source Code' folder here.