Table of Contents
This page will go over the process of starting a bare-metal project for Freescale's Freedom KL46Z Development (FRDM-KL46Z) board. You are free to use any compiler, but we will use KEIL µVision version 5.18a (downloadable here). This project will attempt to be as simple as possible, using very little outside code and is intended to be a basis for learning how to access and manage the peripherals of the board through register manipulation. The end result will be a blinking LED.
For reference I will be using certain fonts or styles to denote specific things, shown in the following table
|Bold||Exact wording or button|
Click on Next to continue
|Italics||Filename or Path||Now open My_File.c|
|Register names||Set the 9th bit in |
Kinetis KL46 Reference Manual (this is the Microcontroller (MCU) that is on the dev board) KL46P121M48SF4RM.pdf
FRDM-KL46Z User's Manual FRDM-KL46Z_UM.pdf
FRDM-KL46Z Schematic FRDM-KL46Z_SCH.pdf
MDK-ARM which has Keil µVision version 5.18a from KEIL's download page
KL46_SC and FRDM-KL46Z_QSP from Freescale's Download page for the FRDM-KL46Z
P&E OpenSDA drivers from Pemicro
Installing FRDM-KL46Z driver interface
Start by plugging in the FRDM-KL46Z to the computer via the OpenSDA Mini-B connector (See to right, click to enlarge). The FRDM-KL46Z will be powered by this. The board comes with a mass-storage device Flash Programmer OpenSDA Application installed and will have a volume label FRDM-KL46Z. Windows should install the Serial Port drivers automatically. If it does not, open Device Manager, Locate and right-click on OpenSDA – CDC Serial Port. Then select Update Driver Software, Browse and select the FRDM-KL46Z drive. Click Next to complete the installation.
Last, drag and drop CMSIS-DAP_OpenSDA.S19 from FRDM_KL46Z_QSP/OpenSDA Applications into the FDRM-KL46Z drive in Bootloader mode. To access the bootloader, unplug the usb from the board. Hold down the reset button as you plug it back in. After dragging in the file in. Once that has copied, unplug the usb, and plug it back in without holding down the reset. The device is now ready to run the CMSIS-DAP debugger.
Starting our project
We'll start this section by opening KEIL µVision5. Our first order of business is to start a new project. Go to Project>New µVision Project... then choose a directory and a name for your project. I will be placing mine in C:\Users\Ethan Hettwer\Example_Project\Example_Project.uvproj. I strongly recommend making a new folder for the project.
Next we need to choose a target device. We are using the Freescale MKL46Z256xxx4 (in our case xxx = VLL, or the 100 pin package). This can be found in Freescale Semiconductor > MKL46Z256xxx4. Click OK, a list of software components will appear. Check CMSIS > CORE and DEVICE > STARTUP, Click Resolve, and then OK.
Next we will need to add a c file. To do this, right click on the Source Group 1 folder and click Add New Item to Group 'Source Group 1'.... This will allow us to create a new file to be included in the project. As this file is intended to be holding code, it needs to be a C file. Select this, then name it, I will be using main.c. To enable components of the MCU we will be manipulating base registers. To help with this, right click in the code window and select Insert '#include <MKL46Z4.H>. This header file holds register names and "masks" which allow more intuitive use of those registers. If you wish to look at the entirety of the file, right click on <MKL46Z4.H> and select Open Document <MKL46Z4.H>. For the most part, the masks are named based on the KL46 reference manual in the form 'CHAPTER NAME'_'REGISTER NAME'_'BIT NAME'_MASK, all in caps. An example of using masks to modify a register is shown below. There are many ways of doing this both with and without masks, I will show a few and explain why some might be better than others.
Note the use of |= in this section. It is a useful command that takes the first argument, logical ORs it with the second, then returns the value to the original. This is used to set bits in a register without changing others.
Masks can be useful for a multitude of reasons. The first (as shown in the example) is readability. It allows the reader to quickly understand what's happening. By using a common naming scheme you can see what register and which bit in that register you are setting. A second reason is portability. Assuming perfectly written c code, by changing the header file you could port to another device seamlessly.
Messing with registers
Now we're going to start getting into the nitty gritty of the code.
As I prefer to make my code modular and would rather make function calls than do it all line by line I will make a function to initialize my LEDs. This can all be done in main, but leaves slightly more cluttered code. To use one of the LEDs we only need a GPIO (General Purpose Input/Output) pin. This involves using a couple of different registers, listed below (from the KL46 Reference Manual).
The FRDM-KL46z User Manual is incorrect on which LED is on which port. We will be blinking the green LED which is on PTD5.
- System Integration Module (SIM) - This gives control of which peripherals are clocked. By leaving them without access to the clock we save power, but leave them inoperable.
- System Clock Gating Control Register 5 (SIM_SCGC5) - This register holds the clock enable for the various ports on the MCU. IN ORDER TO USE ANY PINS ON A PORT THIS MUST BE ENABLED FOR THAT PORT. For example, to use the red LED on pin PTE29 we must first enable the clock to Port E in SCGC5.
- Port Control and Interrupts (PORT) - This register determines which pins are used for which peripheral. Some of the power of the MCU is customization, and this is where that is done. The pinout is located on page 172 of the KL46 Reference manual and shows what mode must be selected for a specific purpose.
- Pin Control Register (PORTD_PCR5) - We will be using the green LED on the FRDM-KL46, so we will need PORTD (the port we're on), PCR5 (the pin we're on) and configure it to GPIO.
- Gerneral-Purpose Input/Output (GPIO) - Once a pin is configured as GPIO this register determines whether it is an input or an output. This register is also used to read from and write to GPIO pins.
- Port Data Direction Register (GPIO_PDDR) - This register configures GPIO as inputs or outputs. Pin number is the same as bit number, 0 is defined as input, 1 as output. So to define PTD5 as an output we would set bit 5 of PTD.PDDR to 1.
- Port Data Output Register (GPIO_PDOR) - This register will change the output of a pin to the value given. So an input of 0 will change the bit to a 0, an input of 1 will change the bit to a 1.
- Port Data Set Register (GPIO_PSOR) - This register will set the output of a pin. An input of 0 will not change the current value of the pin, while a 1 will set the bit to a 1.
- Port Data Clear Register (GPIO_PCOR) - This register will clear the output of a pin. An input of 0 will not change the current value of the pin, while a 1 will clear the bit to a 0.
- Port Data Toggle Register (GPIO_PTOR) - This register will toggle the output of a pin. An input of 0 will not change the current value of the pin, while a 1 will change the bit to the inverse of its current state.
- Port Data Input Register (GPIO_PDIR) - While we are not using this, it will return the value of an input port in a 32 bit format. The value of a single pin can be obtained by taking that bit from this register.
So now that we have that, I'll throw some example code to show how I configured each of these registers.
Now we can write the function to implement the blinking. This can be done in a multitude of ways, which I will be showing below
3000000 is an arbitrary number for the wait loop. You can make this larger or smaller to control blink interval.
The last thing we need for code is an actual main function. We will keep everything in a while loop to make sure we never leave main as that could cause undefined behavior.
This is it for the code that we need.
Compiling, programming and debugging
To program our MCU we need to choose our debug option. To do this, click on the Options for Target... button in the menu bar, go to the Debug tab and select CMSIS-DAP Debugger, then OK.
To compile our program and load it we click on Rebuild in the top menu bar then the Download button. The green status light on the FRDM-KL46Z should light up, then go dark, and your LED should be blinking.
That's it! If something isn't working there are a few places to look. first, check your registry initialization. Double check to make sure you enabled the clock to Port D, set PTD5 MUX to GPIO, then set the GPIO pin to output
Next, check the blink function you've chosen. Remember that clearing the Port Data Output Register (PDOR) turns the LED ON. If you're just testing and trying to turn the LED on, make sure you're setting the register to 0, not 1. If you are using the PCOR command you must place a 1 in the bit you want to clear. This seems counterintuitive, but it's just telling the computer which bit to clear, not what to put there.
Another issue could be based upon the MKL46Z4.h file. KEIL MDK-ARM V5 comes with one version of MKL46Z4.h, while the FRDM starter code comes with another. This tutorial was written for the version that comes default with KEIL. In order to make it compatible with the version in the starter code, the way we access registers changes. The new revision of MKL46Z4.h targets registers differently, and I'll list some of the changes below:
In general, we replace -> with an underscore. This changes SIM->SCGC5 to SIM_SCGC5. For all GPIO changes, such as PTD->PSOR we change it to GPIOD_PSOR. It also allows new variable masks. For example, when we wanted to clear PTD5, we would have used GPIOD_PCOR = (1u << 5) before. Now we can use GPIO_PCOR_PTCO(x), where x is the bit we want to clear. This does not change the functionality of the code, but can make it more readable and portable (see above for use of masks).
This is a start for getting used to register manipulation on the KL46Z, and is just the beginning. I recommend you try to get the red LED on PTE20 working in a similar fashion, to test the understanding of the tutorial here. It involves the same steps, but using Port E instead of Port D, and pin 20 instead of pin 5.
Another tutorial is available for getting the LCD Display working available here Using the Segment LCD Controller on the Kinetis KL46.
Some other general tips for coding
- Use comments liberally! The compiler removes them all, so it doesn't increase the size of your code, but drastically improves readability. Label what each function does, or what a line of code is accomplishing. It also helps in debugging to go through and check what you want the line of code to do vs. what it actually does. It's very easy to make a mistake like expecting GPIOD_PSOR to turn an LED on, as we're setting the pin high.
- USE THE DEBUGGER. Often setting breakpoints in code is very helpful to find where an error is! If the code locks up, using the debugger to find the error is often key, and very helpful.
- Try to avoid hardcoding. Above where I've used something like "PTD->PTOR = (1u<<5)" I might be better served making a variable at the start of my code called "Green_LED_Pin" and setting it to 5. In this way I can make the line of code "PTD->PTOR = (1u<<Green_LED_Pin)". By doing this I 1) Increase readability (are you sensing a theme?) and 2) allow a rapid change. In this application the pin we're using is static, but in other applications we can change which pin we use. If we use a variable, all we have to do is change the value of that variable and we change the pin we're on instead of having to go through and check every line of code.
Contact and Other Info
I hope you found this article helpful and insightful. If you have any issues or think something should be added to this article please contact me at firstname.lastname@example.org.
A thank you to our sponsor at digikey.com. If you want to help out the EE wiki, click a link or two to the digikey website or just browse around other EEwiki articles. Continued interest helps show that the EEwiki is a valuable tool and worth supporting.