Introduction
FRDM-KL25Z is an interesting ultra low cost board with 32-bit microcontroller. What makes it interesting for the Do-It-Yourself community is low price (about $13) and also the compatibility with the Arduino pinout. If you are Arduino user like me, you will probably also feel interest if you hear about a board which could, for less than half the price of standard Arduino board, give you a 32-bit ARM MCU running at 48 MHz with 128 KB of FLASH and 16 KB of RAM memory, on-board accelerometer and more. But as you might have expected, there is a "catch". The FRDM board has the same layout of pins, so you can connect the Arduino shields to it, but there is no software compatibility. You cannot use the Arduino API functions such as digitalRead, delay, etc. and you cannot program the board from the Arduino IDE. I was thinking it would be nice if you could... And this article is my first step in this direction.
What to expect
The above text could make you think that I made all the work of porting Arduino to the FRDM-KL25Z platform. Unfortunatelly, not so. I should clearly state at the beginning what to expect. This article presents software library (or framework) which allows you to write your programs using the same functions which are available in Arduino API. It does not allow you to use the Arduino IDE to program the FRDM board. It also does not contain the Arduino libraries, just the basics API. The IDE used is Freescale's Kinetis Design Studion (KDS), which is an IDE based on Eclipse and very much just simplified version of the well-known CodeWarrior IDE. It is free, which is nice. You can download it from here.
Target audience
I think the primary audience is a person who likes programming and is not afraid of C, but is not too much into creating harwdare and likes the comfort of the Arduino-like boards and shields. I would say it is for programmers who want to use Arduino-like board with high power and interesting features.
This library may be hard to use for a beginner Arduino user.
What's new
In June 2015 revision of this article I updated the text to allow using the library in new version of the Kinetis Design Studio IDE (3.0.0). Updated source code is also provided. For details please see the release notes file in the source package.
In Feb. 2015 updated version of this article and the library, I added the pulseIn
and tone
functionality. I somehow missed this in the first version even though it seems very useful. In case your are not familiar with the Arduino API, the pulseIn
function reads the length of a pulse on a pin. The tone
function generates square wave signal on a pin.
However, I decided not to 'emulate' these two functions exactly, because they have one serious limitation - with pulseIn
you can only measure the pulse on one pin at a time, blocking the program from execution. Also, you can only generate the square wave signal with tone
on one pin at a time. This may be fine for experimenting and playing, but for serious projects it will be very limiting. So I needed more robust solution. And once I had it, it seemed useless to build the pulseIn and tone wrappers around it with their limits. The solution I added to this library is new high-level (read easy-to-use) driver called waveio
. Please see the pulseIn, tone and Servo chapter at the bottom of this article for more information.
Here is just overview of the features of the waveio
driver:
- measure and generate signal on up to 10 pins at the same time (any combination of inputs/outputs).
- output signal can have any duty, not just 50% (square wave).
- convenience functions for controlling servos and reading RC PPM signal (the signal which controls servos in RC models).
- convenience function for reading input pulse (for example, from the Parallax Ping ultrasonic range sensor).
In the attached zip file you will find new version of the library with the new waveio
driver and waveio example project which shows how to use it.
Background
If you are interested in the FRDM-KL25Z board hardware, you can find many sources on the internet. Short overview is also included in my older article here on codeproject - Writing your first program for the Freescale Freedom platform in CodeWarrior.
If you decide you would like to have the same functions which are available in Arduino for your microcontroller board, you have basically two options:
- Take the Arduino sources and modify it for your board, or
- write your own functions and add Arduino-compatible 'wrapper' functions at the top
I did not want to use the option 1, even though it could be interesting - to create a new "Arduino clone". But the benefits seems small compared with the problems. In fact, the only benefit I can see is that it would be available to broaded audience - all the people who use Arduino could use it. Compelling, but there are the problems: First, I did not feel I could handle all the work, such us integration into the IDE alone, so I limited myself to just creating the software. Second, I do not like the way Arduino API is designed internally. I did not want to adhere to design which I (and I am not alone) do not consider good. And last, the Arduino IDE is very limited in functionality, mainly, there is no debugger available. Debugging your program for an ARM microcontroller by printing to serial line seems inappropriate.
So I decided to write a library designed in my own way and add the Arduino API as a 'thin wrapper' at the top. This way you can use Arduino functions if/when you need, but you can also move to more powerful functions when you are ready. I named my library MSF Lite. The MSF stands for Microcontroller Software Framework. More on this later.
Using the code
To use the library in your projects do the following:
- extract the attached zip file to your disk. For example, to c:\msf_lite.
- start with some example program from msf_lite\examples.
You will need:
Note about the board and OpenSDA application
I assume you have the FRDM-KL25Z board and USB cable to connect it to your computer. I am not sure which version of the OpenSDA application (the 'bootloader') your board will have. It may be a good idea to update the firmware to the latest version. You can download it from P & E Micro. There is Open SDA Firmware link at the top of the page as well as instructions for updating the firmware in your board. The zip package you download from the P & E Micro website contains many applications for different boards. You should use the MSD-DEBUG-FRDM-KL25Z_Pemicro_v114.SDA which is the debugger, virtual serial port and mass storage all in one.
Opening example programs
Please note that tutorial on opening example projects can also be found in pdf and html format in msf_lite/doc. It may be more up to date than this article.
Example programs can be found in msf_lite\examples\kds\frdm_kl25z folder. The names of the examples which use Arduino API start with "arduino_", the other examples are "plain" C with the MSF library functions.
Here is how to try the example programs:
Start Kinetis Design Studio and from the File menu select Import...
In the Import window expand the General folder and select Existing Projects into Workspace:
In the next window select the 'Select root directory' option and Browse to msf_lite\examples\kds\frdm_kl25z. Select the folder for the example program you wish to import, for example, arduino_blink, and click OK.
TIP: You can also select the parent frdm_kl25z folder and import all the examples at once.
The project contained in the selected folder should appear in the 'Projects' area in the Import window.
Please make sure the 'Copy projects into workspace' box is NOT checked!
Click the 'Finish' button and the project should be imported.
You can now open the main.c file in the Sources folder to view the code.
To build the program, click the icon with the hammer in toolbar:
You could also right-click the project and select 'Build project' from the context menu.
The project should build without errors.
Starting the program
We need a Debug configuration to be able to download (and debug) the program.
In the revision of this library for KDS 3.0.0. (June 2015) the debug configurations were added to the example projects, so you should not need to create them. But in case you encounter some problems with the included configuration, you can still create a new one, as described here.
Right-click the project in the Project Explorer and select Debug As... > Debug Configurations... from the context menu.
In the Debug Configurations window select the 'GDB PEMicro Interface Debugging' and then click the 'New Launch Configuration' button in the upper left corner of the window.
In the right-hand side of the window some tabs appear. First select the Main tab. Click the 'Search Project...' button below the 'C/C++ Application box' and select the .elf file which you will see - for example, arduino_blink.elf.
You should now see the selected .elf file (executable) in the C/C++ Application field.
Next, select the Debugger tab.
In the 'Interface' combo box select 'OpenSDA Embedded Debug - USB Port'.
In 'Device name' select 'KL25Z128M4'.
Make sure your FRDM-KL25Z is connected to your computer.
Now Click the 'Debug' button in the bottom right to start debugging.
If everything goes well, the KDS should switch to debug perspective and you should see the program stopped at the beginning of the main
function. You can now click the 'Resume' button to start the program.
For the arduino_blink example project you should see the blue LED on the board blinking.
To stop the program use the red 'Stop' button. You will then want to switch back from the Debug perspective to the C/C++ perspective using the 'C/C++' button in the upper right corner of the IDE window.
Please note that if you want to try another example, you will need to create new debug configuration in the same way as described above.
You will find description of each example program in the comment on top of the main.c file. The IDE will display this comment in a 'collapsed' state by default, so to see the comment, click the plus (+) button in the editor.
Creating your own project
For instructions on how to create your own project with MSF lite please see msf_lite\doc\kds_howto3.0.0.txt file.
Configuring your project
There are many options you can configure in your project. You can do so by editing the msf_config.h file which is located in the Sources folder in your project (open it from the KDS IDE).
You will find several macros in this file. The most important are <var>F_CPU</var>
and <var>F_BUS</var>
. By these you define the CPU speed you will use.
Please note that the purpose of these macros is to inform the MSF library about the CPU frequency you have set. You do not define the CPU speed by changing these values. You just inform the library which CPU speed are you using.
The CPU speed can be set by defining CLOCK_SETUP symbol with value betwen 0 and 4. In the new version of KDS, 3.0.0, the method for this changed. The CLOCK_SETUP symbol is no longer defined in the source code provied in KDS. If you do not define it yourself, default setup 0 will be used (20.97 MHz CPU clock). You can define this symbol in your project settings: C/C++ Build > Settings > Cross ARM C Compiler > Preprocesor.
In the Defined symbols list enter: CLOCK_SETUP=1
To see the clock options defined in KDS, open your project/Includes folder > system_MKL25Z4.h file. Predefined clock setups are in a comment near the top of the file.
The example projects provided with this article use clock setup 1, which is 48 MHz, but they should work with all the available frequencies. Just make sure the value you set by defining CLOCK_SETUP symbol is in sync with the values defined in msf_config.h. You will find all the available (and supported) values commented out in the msf_config.h file, so you can just un-comment the values you need. Please note that this is valid for CLOCK_SETUP options available in KDS version 3.0.0 and 1.1.1. I hope the values will not change in future versions of the IDE.
In the older version of KDS (1.1.1), the CLOCK_SETUP was defined in system_MKL25Z4.c file, which you can find in Project_Settings\Startup_Code folder in your project. At the top of this file there was a definition of the CLOCK_SETUP symbol, for example, #define CLOCK_SETUP 1.
Documentation
There is doxygen generated documentation for the source code included in the msf_lite/doc/doxygen folder. Open the index.html file located there. For the Arduino-compatible API see the next section.
There is also tutorial on creating new project with MSF Lite in msf_lite/doc/kds_howto3.0.0.txt and tutorial on importing the example projects in msf_lite/doc/import_example_project.pdf.
The Arduino-compatible API
I don't want to duplicate the documentation of the original Arduino API, so I will just list the supported functions and add some comments where appropriate.
Digital input/output
pinMode
, digitalRead
and digitalWrite
are available with the same interface as in Arduino. Here are the prototypes:
void digitalWrite(int pin, int val);
int digitalRead(int pin);
void pinMode(int pin, int mode);
The pin
is a simple integer number, same as in Arduino. These digital I/O functions are implemented in a similar way as my fast version of the digital I/O for Arduino, so they should work pretty fast. If you need best performance, you can use the native MSF functions, such as msf_pin_write. See the MSF Lite API section below for more details.
Analog functions
Analog functions are supported too. I include the analogWrite function here also, even though we all know it is not really analog output but rather a PWM output.
void analogReference(int type)
analogReference works the same way as original, but only DEFAULT and EXTERNAL reference types are supported. There is no internal reference in the ADC of our MCU. Unless you make a change to your hardware, the two values have the same effect for the FRDM-KL25Z board - the reference is the power supply voltage, which is about 3V. This is because, as the documentation says, the VREFH (AREF) pin is connected to power supply (3V) on the board and if you want to use external voltage, you have to cut trace SH1and put wire (0R resistor) in place of R3.
int analogRead(int pin);
Same function as original, but the value is 16-bit ( range 0 thru 65535).
void analogWrite(int pin, int value);
The pin
is the pin number of the digital pin as used in Arduino. For mapping of the Arduino pin numbers to the real MCU pins (port + pin) see arduino.h file in msf_lite\board\frdm_kl25z. You will find definitions like
#define PD2 (GPIO_D4)
which means the arduino digital pin 2 is actually pin D4 of the microcontroller.
The value
is the duty cycle of the PWM signal between 0 and 255. This is the same interface as original, but the functionality is much extended. Unlike on standard Arduino board, where only few pins can really generate PWM signal, almost all the pins on the FRDM-KL25Z can do so (all except pins 14 and 15). The frequency of the PWM signal can be set by #define in the configuration file msf_config.h from about 250 Hz up to 32 kHz. You will find this configuration file in each example project in the Sources folder. Search for MSF_AWRITE_500HZ
.
For the pins which cannot generate PWM (14 and 15) the function does nothing. This is different from the Arduino, which for such pins set the output pin low if the requested duty is between 0 and 127 or high if it is above 127. I think such functionality is useless and just compilecates the code.
Time functions
uint32_t millis();
uint32_t micros();
void delay(uint32_t millis);
void delayMicroseconds(uint32_t micros);
Same function as in original. The resolution of the micros
function is 1 microsecond.
Note that delay
should not be called from interrupt service routines (ISR). It is safe to call delayMicroseconds
from ISRs. The delayMicroseconds works reliably for delays from about 2 microseconds up. You can also use MSF_DELAY_1US and MSF_DELAY_5US macros for short fixed-time delays.
Serial class
The serial class provides serial communication (UART) in Arduino. I did not want to use C++, so the Serial in MSF lite is not a real class, but just a structure with function pointers. It may sound scary, but there is no difference for the user - it is used in the same way as in Arduino. For example you write:
Serial.begin(9600);
The functionality of the class is very limited compared to the Arduino version. This is both due to the decision not to use C++ and lack of my strong will to implement the advanced functions.
The main difference is that there is no single print function which would print anything you give to it. This is not easy to do in C (unlike in C++). That is why I created separate functions for printing the most common data types.
Also, it can be used only with the UART0 (the pins 0 and 1). There are no Serial1 or Serial2 'objects', even though there are in fact 3 UART modules in the microcontroller. For the other UARTs the 'native' MSF lite driver can be used.
Here is complete list of the MSF lite Serial 'class' functions:
void begin( uint32_t baudrate);
Supports baudrates 2400, 4800, 9600, 19200, 38400, 57600, 115200. For invalid baudrate will default to 9600. Always uses 8 data bits and 1 stop bit, no parrity. This cannot be changed. Native MSF lite driver <var>Driver_UART0</var>
can be used instead of Serial, if such advanced functionality is needed.
void end(void);
Does nothing in fact.
void print(const char* str);
Print text (string) to serial line.
void println(const char* str);
Same as print but also print a new line after the text. You can also include "\n" in the string given to print to make a new line.
void printint(int val, int format);
Print integer number in given format. The format can be DEC, HEX, OCT. BIN format is not supported.
void printfloat(float val, int decplaces);
Print real number with given number of digits after decimal point. Note that the floating point numbers must be enabled for (s)printf. To do this, you need to add this flag:
-u _printf_float
to the C/C++ Build > Settings > Cross ARM C++ Linker > Miscellaneous > Other Linker Flags in your project properties.
int available(void);
Get the number of bytes (characters) available for reading from the serial port. This is data that's already arrived and stored in the serial receive buffer (which holds 64 bytes).
int peek(void);
Returns the next byte (character) of incoming serial data without removing it from the internal serial buffer. That is, successive calls to peek() will return the same character, as will the next call to read(). Return -1 if no data is available.
void flush(void);
Waits for the transmission of outgoing serial data to complete;
int read(void);
Reads incoming serial data. Returns the first byte of incoming serial data available (or -1 if no data is available).
int readBytes(char* buffer, int length);
Reads characters from the internal buffer of serial driver into given buffer. The function terminates if the determined length has been read. Return number of characters placed in the buffer. A 0 means no valid data was found.
int readBytesUntil(char terminator, char* buffer, int length);
Reads characters from the internal buffer of serial driver into given buffer. The function terminates if given terminator character is found or if the given length has been read. Returns number of characters placed in the buffer. A 0 means no valid data was found.
void write(int val);
Write byte to serial line. This is limited version of the Arduino function; it just sends 1 byte. If you want to send a string, use Serial.print().
Other functions
The <var>map</var>
and <var>constrain</var>
are also supported and work in the same way as in original.
The pulseIn
and tone
functions (and the Servo class) functionality are supported indirectly with the new waveio
driver added in the February 2015 update to this article. See below for details and examples.
MSF lite
As already mentioned, my library is named MSF lite and the MSF stands for Microcontroller Software Framework. This is surely exagerated name for a rather simple library for one MCU, I admit. But the name comes from my "long-time" project (or a dream) of a unified API for microcontrollers. This would be a topic for another article, but just to give you some idea what I mean: imagine there would be set of functions which you could use on any microcontroller... The MCU manufacturers do not seem to put much effort into something like this. I can understand them - they like to tie the customer to their MCUs. And having no API, specific private API or even bether, a graphical development tool, is a good way of doing this. It would be real pain to migrate your project to another MCU brand if you have to rewrite all the code down to hardware level. Is this good for the customers? Probably not, but who cares.
But the times they are a changing. The Arduino is a nice example. The MCU manufacturers never could/wanted to settle some standard layout for an evaluation board; the users did it for themselves with Arduino. Now the MCU manufacturers are starting to suport Arduino; they make their evaluation boards with Arduino-compatible layout of pins so that the Arduino shields can be connected.
Another example is the ARM processor architecture. Is is quickly becoming the standard for MCUs. It seems soon everything will be ARM. So in hardware, the standardization seems to come true.
In software the situation is not so optimistic. Sure there is the Arduino API used as much as the Arduino hardware, but it lives in its own community and has little appeal to advanded.programmers or professionals. It is too simple and too badly designed (and not efficient) to become a widely used standard. There is also the CMSIS standard by ARM, which suggest software API to some extent, including peripheral driver definitions. But it is for ARM-based devices only and not really implemented by all MCU manufactuters. I think an open source standard will be needed.
I created some basic design of the MSF and implemented a version for the FRDM-KL25 board which you will find underneath the Arduino API in this package. That is why there is 'lite' in the name - this is a light-weight version of the real API which MSF should be.
Internal design of the library
The following picture shows the layers of the library.
At the bottom just above the microcontroller hardware, the CMSIS-Core component is used. This code is provided by the MCU vendor, Freescale in our case. The CMSIS standard is designed by ARM and defines low-level things, such as how the hardware registers are defined in C language (register overlay structures) and some code functions such as SysTick timer.
Above this level I would like to use the CMSIS-Driver components, which is another part of the CMSIS standard defined by ARM. Unfortunatelly, Freescale does not provide this component. It provides only some basic drivers in the sample code package for the board. As I did not like the way those drivers are written and prefer to stick with standards as much as possible, I decided to create my own version of the CMSIS-style drivers. I stress the 'CMSIS-style' here, because I did not really take the job of the MCU manufacturer to implement the CMSIS driver layer. I just tried to use the same API. But do not expect full functionality as defined by the CMSIS standard from my drivers please.
For peripherals which the CMSIS-Driver standard does not cover (for example, ADC or timer) I created my own definition of the driver interface in a similar style.
At the yet higher level there are MSF drivers, which should offer unified basic functionality for all platforms.
At even higher level there are MSF convenience global functions, such as msf_delay_ms() which should allow easy implementation of simple, comonly used operations. This is sort of my replacement of the Arduino API.
At the very top there is the Arduino API wrapper. It is in many cases just a simple macro or inline function which calls the msf function below. But in some cases, such as the Serial 'object' there is quite a lot of code too.
Native MSF API
As mentioned earlier, the Arduino API is actually just a thin wrapper around the native API of the MSF library and its drivers. It would take too much space to decsribe the API of all the drivers here. Please refer to the \doc directory in msf_lite and/or to the source code itself. There is also Doxygen documentation generated from the code in the doc/doxygen/index.html.
Here is overview of the main functions:
MSF global functions
msf_init(uint32_t param)
Initialize the MSF. Should be called at the beginning of your program.
GPIO (digital input/output)
msf_pin_direction(pin, dir)
Set the direction of a given pin to input or output. Does not change pull-up settings. The <var>pin</var>
parameter is one of the values defined in msf_mkl25z.h. The name of the pin is based on the original name in the microcontroller. The format is GPIO_[pin name], for example GPIO_A0
or GPIO_E29
.
The direction is either input or output. See gpio.h for details.
msf_pin_pullmode(pin, mode)
Enable/disable pull-up resistor on given pin. Does not check or change the pin direction. The pin should be input. The pin parameter is described above in msf_pin_direction. The mode can be pull_up or pull_none.
msf_pin_write(pin, value)
Set pin to high or low state. The value is true or false. Does not check or change the pin direction. The pin should be configured as output!
msf_pin_read(pin)
Read logical level at given pin. Returns true if the level is high, false if it is low. Does not check or change the pin direction. The pin should be configured as input!
msf_pin_toggle(pin)
Toggle the state of given output pin. The pin should be configured as output!
Time measurement and delay functions
uint32_t msf_millis(void);
uint32_t msf_micros(void);
void msf_delay_us(uint32_t micros);
void msf_delay_ms(uint32_t millis);
These are time functions similar to Arduino.
MSF_DELAY_1US(void)
MSF_DELAY_5US(void)
Very short delay functions - implemented as inline functions in delay_util.h.
Functions for serial communication
void msf_print(const char* str); void msf_print_char(char c);
void msf_printnum(uint32_t number);
void msf_printhex(uint32_t number);
void msf_printf16(const char* str, const char* format, uint16_t data);
void msf_printf32(const char* str, const char* format, uint32_t data);
void msf_printf_real(const char* str, const char* format, double data);
char msf_read_char(void);
bool msf_char_available(void);
Analog functions
uint16_t msf_analog_read(Analog_pin_t apin)
Read analog value. The <var>apin</var>
parameters is one of the values defined in msf_mkl25z.h. Pin name is AIN_[real pin on the MCU], for example, AIN_E20
. (E20 means PTE20). Note that not all pins can be used as analog inputs. See the <var>Analog_pin_t</var>
enum in msf_mkl25z.h for available values.
pulseIn, tone and Servo (waveio driver)
As mentioned above, the pulseIn
and tone
functions (and Servo
class) are not directly available in MSF Lite, but their functionality is covered by the waveio
driver. You can see various examples of usage in the waveio example project. There are examples of:
- generating signal on an output pin
- generating signal on multiple pins at the same time
- controlling RC servos (similar to sweep example in Arduino)
- measuring distance with the Parallax Ping ultrasonic sensor (reading pulses on input)
- reading signal from RC transmitters (up to 10 channels at the same time)
Here are some typical examples of use.
Generate signal (with any duty) on a pin (or multiple pins)
waveio_init(WAVEIO_RANGE_0_5);
waveio_out_start(WAVEIO_C0, 500, 500); waveio_out_start(WAVEIO_C1, 100, 900); waveio_out_start(WAVEIO_C3, 250, 250);
In the waveio_init
function you provide the range of inputs you wish to use. This tells the waveio
driver which low-level timer drivers it needs to initialize. The ranges follow from the channels available in the physical timers available in the MCU. For the Kinetis KL25Z MCU used in the FRDM-KL25Z board there are three timers called TPM0, TPM1 and TPM2. The TPM0 timer has 6 channels, the other two timers have 2 channels each. That is why the available ranges for the waveio_init are 0 thru 5 (channels belonging to TPM0), 6 thru 7 (TPM1 channels) and 8 thru 9 (TPM2). You can combine any of the ranges, for example:
waveio_init(WAVEIO_RANGE_0_5 | WAVEIO_RANGE_6_7 | WAVEIO_RANGE_8_9);
To start outputing the signal you can just call waveio_out_start
after the initialization.
The 1st parameter (WAVEIO_C0
in the example above), is the channel you wish to use. The channel is directly mapped to a pin. In this case the channel 0 is pin PTD0 of the MCU, which is pin 10 in the Arduino pin layout. Note that the mapping can be changed in the project configuration file msf_config.h.
The other two parameters are the lengths (in microseconds) of the half-waves of the signal you wish to generate. I used this method rather than frequency and duty as it seems more flexible and is easier to handle in the code. I hope it is still reasonably easy to use. For example, to generate signal with frequency 1 kHz, you need to obtain period of 1 ms or 1000 microseconds (us). So the two values given to waveio_out_start
must make 1000 in total. If you want square wave (50% duty), both the parts are the same, so you will give the function 500 and 500. If you like equations, here they are:
Period_in_us = 1 000 / frequency_in_kHz
Frequency_in_kHz = 1 000 / period_in_us
Measure signal on a pin
waveio_init(WAVEIO_RANGE_0_5);
waveio_in_attach(WAVEIO_C0);
waveio_in_start(WAVEIO_C0);
while(1) {
err = waveio_in_read(WAVEIO_C0, &a, &b);
if ( err == WAVEIO_NO_ERROR )
;
msf_delay_ms(1000);
}
When measuring input signal, you also start with initializing the waveio driver. Next you need to call waveio_in_attach
to configure the pin into timer input mode. This is only needed once in the program. After the pin is attached, you can start measuring the inputs on this pin by calling waveio_in_start
. The driver will measure the input signal in the background and you can call waveio_in_read
at any time to read the latest measured values. The function will provide you with the lengths of the two parts of the wave on the input. The a
and b
variables in the example receive these lengths. These are simple 16-bit unsigned integers, just passed to the function by pointers so that it can store the output values into them.
Measure a pulse on a pin (waiting for the pulse)
waveio_init(WAVEIO_RANGE_0_5);
waveio_in_attach(WAVEIO_C0);
while(1) {
a = waveio_in_pulse_wait(WAVEIO_C0, 1000);
if (a == 0)
msf_print("Timeout.\n");
else
{
msf_print("Pulse: ");
msf_printnum(a);
msf_print(" us \n");
}
msf_delay_ms(1000);
}
This example shows the waveio_in_pulse_wait
function which waits for a pulse on the input pin (with timeout). It then returns the length of the pulse in microseconds. Note that you do not need to call waveio_in_start
before calling this function. Just initilize the driver and attach the channel you want to use and then call waveio_in_pulse_wait
.
Control servo (sweep example)
waveio_init(WAVEIO_RANGE_0_5 );
for ( angle = 0; angle < 180; angle++ ) {
waveio_out_servo(WAVEIO_C0, angle);
msf_delay_ms(20);
}
The waveio_out_servo
function can be used to control servos. It is in fact just simple wrapper for the waveio_out_start function which takes the desired angle for the servo between 0 and 180, just like the Arduino servo class and converts it to the appropriate pulse length.
If you need finer control over the inputs or outputs than the waveio
driver offers, you can use the low-level TPM timer driver directly. An example of such use for capturing input signal is shown in the input_capture example which is also new to this release of MSF Lite.
MSF CMSIS-style drivers
This section describes some features of the low-level, CMSIS-style drivers used in MSF. These drivers can be useful for advanced users.
You can think of each driver as a C++ class (in fact it is a C structure with function pointers). The driver for UART is one example of such class, driver for timer is another example.
There may be multiple instances of the driver class. For example, if there are three UART modules in the MCU, there will be three instances of the UART driver. You call the driver functions by writing [driver instance name].[function name]. For example, Driver_UART0.Initialize
. This is how CMSIS defines the drivers and I use this concept too.
To find out which functions are available for each driver, look into the drv_[name].h file (for example drv_uart.h or drv_adc.h). In this file, you will find definition of the driver access structure, such as:
typedef struct _MSF_DRIVER_USART {
uint32_t (*Initialize) (UART_speed_t baudrate, MSF_UART_Event_t cb_event);
uint32_t (*Uninitialize) (void);
uint32_t (*PowerControl) (MSF_power_state state);
...
} const MSF_DRIVER_USART;
The Initialize
, Uninitialize
and PowerControl
are the names of the driver functions.
You also need to know the name of the driver 'object'. This you can find in the same file at the bottom. There are lines like these:
extern MSF_DRIVER_USART Driver_UART0;
extern MSF_DRIVER_USART Driver_UART1;
extern MSF_DRIVER_USART Driver_UART2;
For example, to initialize the UART0 module in the MCU, you will write:
Driver_UART0.Initialize();
To find the information about each of the driver functions, please look into the 'uart_kl25.c' file. Here you can find the function, such as UART_Initialize with complete comment.
UART driver
Instances for all three UARTs available in the microcontroller are provided. They names are:
Driver_UART0, Driver_UART1 and Driver_UART2. The driver supports both blocking (polled) operation and interrupt based operation.
See drv_uart.h for details.
Timer driver
There is driver for the TPM timer modules in the microcontrollers. The names of the driver Driver_TPM0, Driver_TPM1 and Driver_TPM2. Many features of the timer are supported such as PWM, input capture and output compare.
See drv_tmp.h for details.
ADC driver
This driver is for the analog-to-digital converter (ADC). There is only one ADC in the KL25 microcontroller, so there is only one instance of the driver: Driver_ADC0. See drv_adc.h for details.
History
2015-01-13 First version.
2015-02-17 Added waveio driver and examples - pulsein and tone functionality.
2015-06-19 Example projects modified to work with KDS IDE version 3.0.0.