Using the FRDM-KL25Z as USB Keyboard


I miss my old DELL laptop. Ok, the new one I received from IT services is not bad. It is faster and has a better screen. But I’m not really happy with the new keyboard. With the previous keyboard I was able to do a ‘PrtnScrn’ with a single key press. With the new one I need to press Fn + PrntScrn. And this is impossible to do with one hand:

Impossible to reach Prnt Scrn

Impossible to reach Fn+Prnt Scrn with one hand!

Yes, I have two hands ;-). But many times I need to do ‘print screen’ while having my other hand on the mouse :-(.What else can I do?

Exploring Options

Of course there are several options:

  1. Call someone in my office and say “hey, can you give me a helping hand?”. Works, but my co-workers will hate me for this.
  2. Reconfigure my laptop keyboard mapping. Doable, but then the writing on the keyboard does not match the reality any more which can be confusing.
  3. Remapping the print screen functionality in SnagIt (the screen capture tool I’m using). That would be too easy 😉
  4. Using the FRDM-KL25Z board as USB keyboard

As you might have guessed from this post title: I’m going for option 4 :mrgreen:. This gives me the added value that I can do anything I want: having my own  shortcuts, doing sequences of key press actions, and so on.

In the next steps I explain how to turn the FRDM-KL25Z into a USB HID Keyboard device with CodeWarrior for MCU10.4 and Processor Expert Components. A link to the project is posted at the end of this article.

Processor Expert Components

Make sure you have the latest Processor Expert components loaded. Instructions how to load the components from my GitHub site is explained in this post.

Creating CodeWarrior KL25Z Project

Create a new Processor Expert project in CodeWarrior using the menu File > New > BareBoard Project:

  1. Provide a name for the project
  2. Select the MKL25Z128 under Kinetis L Series > KL2x > KL25Z
  3. Select OpenSDA as connection
  4. In ‘Language and Build Tools Options’, the No I/O option can be selected for reduced footprint
  5. Select Processor Expert under Rapid Application Development
  6. Finished 🙂
USB HID Project Created

USB HID Project Created

USB Clock Settings

The USB clock needs to be set at 48 MHz. For this, I open the Inspector on the Cpu:

  1. Enable ‘System oscillator 0’. This enables it to use the 8 MHz external crystal:

    System Oscillator Enabled

    System Oscillator Enabled

  2. Set the MCG clock settings to PEE (PLL Engaged External) with 96 MHz:

    PEE with 96 MHz

    PEE with 96 MHz

  3. Set the core clock to 48 MHz and the bus clock to 24 MHz:

    48 MHz Core Clock with 24 MHz Bus Clock

    48 MHz Core Clock with 24 MHz Bus Clock

With this I have properly configured the microcontroller to work for USB.

Adding USB Stack

The next step is to add the USB stack. I have created the FSL_USB_Stack Processor Expert component which is a wrapper for the Freescale bare metal USB stack. This USB stack can be used as well with an RTOS like FreeRTOS too. The FSL_USB_Stack component now supports HID, CDC and MSD device classes.

From the Components Library view, I add the component to my project (double-click on the component):

FSL_USB_Stack Component

FSL_USB_Stack Component

By default, it is added for CDC. So I need to configure it for USB HID Keyboard device class.

I select the FSL_USB_Stack and configure it:

  • USB is using Init_USB_OTG_VAR0: this one is used for Kinetis/ARM.
  • Device Class: HID Keyboard Device.
  • In the HID Keyboard field, I choose FSL_USB_HID_Keyboard_Device.
FSL_USB_Stack Configured for USB HID Keyboard

FSL_USB_Stack Configured for USB HID Keyboard

Next, I select the Init_USB_OTG component and configure it:

  • Enabled clock gate (otherwise the USB module is not clocked).
  • Set PLL/FLL as clock source (has to be 48 MHz, that’s why we configured the CPU clock in the previous step for 48 MHz).
Init Component Configured

Init Component Configured

Finally, configure the FSL_USB_HID_Keyboard_Device CPU:

FSL_USB_HID_Keyboard CPU Configured

FSL_USB_HID_Keyboard CPU Configured

Now all the error markers should disappear as I finished configuring the component.

FSL_USB_HID_Keyboard_Device Component

The component has two more settings which are used to report the device to the host:

HID Settings

HID Settings

The name is used in the Windows Device manager:

FSL HID Keyboard in the Windows Device Manager

FSL HID Keyboard in the Windows Device Manager

The component features a ring buffer which is used to store keyboard events to be sent later:

USB HID Keyboard Buffer Settings

USB HID Keyboard Buffer Settings

The buffer entries are 16bit each (2 bytes), because a keyboard event has one byte for the ‘modifier’ (e.g. SHIFT pressed) and the key itself (e.g. ‘a’). More about this later. By default the buffer is set up for 16 entries: so I can buffer up to 16 items (or key changes). If you always send just one key, then you can reduce this number to a lower number, say 4 (to be safe).

USB HID Keyboard Protocol

The HID Keyboard device has to send a report to the host. That report is an array of 8 bytes describing the current key status:

  • Byte 0: Modifier byte which encodes CTRL, SHIFT, ALT and GUI keys (8 bits are defined for this)
  • Byte 1: unused
  • Byte 2-7: 6 bytes with key codes. With this it is possible to report up 6 plus the modifier keys pressed at the same time.

Typically only Byte 0 and Byte 2 are used. So for example to send a USB HID report that ‘a’ is pressed, I send

Byte 0: 0x00 (no modifier)
Byte 2: 0x04 (is the code for key 'a')
all other bytes are 0x00

To send the a report that ‘A’ is pressed (which is SHIFT+a):

Byte 0: 0x02 (left SHIFT key pressed)
Byte 2: 0x04 (is the code for key 'a')
all other bytes are 0x00

It is important to note that these reports are ‘keys are down’ reports. And as long as I do not send a new report, the last sent report is still active (or key pressed). So I need to send a message for the ‘release’ event, which is a report with all bytes zero:

Byte 0: 0x00 (no modifier key pressed)
Byte 2: 0x00 (no key pressed)
all other bytes are 0x00

The Key Usage codes are documented in chapter 10 of the HID Usage Tables of the USB Standard Document.

The FSL_USB_HID_Keyboard_Device Processor Expert component I have implemented makes it really easy to use.

FSL_USB_HID_Keyboard_Device Interface

The component offers the following methods and events:

HID Keyboard Device Interface

HID Keyboard Device Interface

  • App_Task() needs be called periodically. With this the elements from the ring buffer are sent to the USB bus. Call this method after you have used any of the Send methods. This routine returns ERR_OK if the device has been enumerated (is connected to the host).
  • SendStr() can be used to send an ASCII string. This method translates the string into USB HID messages and places them into the ring buffer to be processed by App_Task().
  • SendChar() is the same as SendStr(), but sends a single character only.
  • Send() is used to send native USB HID code. As for the other Send routines, it places the item into the buffer to be processed later by App_Task().

Examples how to use it:

for(;;) {
  if (HIDK1_App_Task()==ERR_OK) { /* run the USB application task: this will send the buffer */
    /* ok: we are connected! */
    (void)HIDK1_SendStr((unsigned char*)"Hello!"); /* send a string */
  }
}

❗ I’m ignoring here the return code of SendStr(). It would return ERR_TXFULL in case the ring buffer is not able to store the string. In that case either increase the buffer size, or send smaller strings and call App_Task() in between.

Once the device is connected and has finished enumeration, App_Task() will return ERR_OK. Then the applications places the string “Hello!” in the ring buffer, which is sent to the host in the next App_Task() call, as if would have typed it on the keyboard.

❗ It happened to me that such test code writes text to my host machine, potentially overwriting my stuff. I recommend to have a notepad window open (with active focus) so the board can write into that space instead overwriting your sources in the editor 😉

💡 you might get a ‘+’ character instead of the ‘!’. This is because my driver does not support different keyboard layouts and mappings. Contributions are more than welcome 🙂

To send a single ASCII character is simple:

(void)HIDK1_SendChar('A'); /* send the A character */

Knowing the USB CDC protocol, it cannot send just the ‘A’ character. USB CDC defines the ‘a’ (and ‘A’) keyboard character with the value 0x04:

#define KEY_A                                  0x04

The information if it is ‘a’ or ‘A’ is encoded as ‘modifier’: if the SHIFT key is pressed or not. And there are several modifier flags available in the header file of the component:

#define MODIFERKEYS_NONE                          0x00
#define MODIFERKEYS_LEFT_CTRL                     0x01
#define MODIFERKEYS_LEFT_SHIFT                    0x02
#define MODIFERKEYS_LEFT_ALT                      0x04
#define MODIFERKEYS_LEFT_GUI                      0x08
#define MODIFERKEYS_RIGHT_CTRL                    0x10
#define MODIFERKEYS_RIGHT_SHIFT                   0x20
#define MODIFERKEYS_RIGHT_ALT                     0x40
#define MODIFERKEYS_RIGHT_GUI                     0x80

So to send ‘A’, I need to send MODIFERKEYS_LEFT_SHIFT and KEY_A.
This information is put together by SendChar() using the hidKeyCode table:

byte HIDK1_SendChar(byte ch)
{
  if (ch&0x7F) { /* only handle 0x00..0x7F */
    Tx2_Put(hidKeyCode[ch]); /* put 16bit value (modifier|code) into buffer */
  }
  Tx2_Put((MODIFERKEYS_NONE<<8)|KEY_NONE); /* send release message */
  return ERR_OK;
}

Note that the above code uses a second Put() with MODIFIERKEYS_NONE and KEY_NONE which is a ‘keys release’ message (actually it says ‘no keys pressed’). The HID protocol sends the actual state of a key. So if I send the information ‘the A key is pressed’ the host assumes that I still have that key pressed until I send another message about the new key. If I would not send that release message, the host assumes that the key ‘A’ is still pressed, and repeats the character. That’s why I send a ‘release’ message right afterwards to tell the host that all keys have been released.

SendChar() and SendStr() perform automatic ASCII to HID code translation. If I want to send HID codes directly, I can use the method Send() of the component:

byte HIDK1_Send(byte modifier, byte key)
{
  return Tx2_Put((modifier<<8)|key); /* put 16bit value (modifier|code) into buffer */
}

So for example if I want to send my ‘print screen’ action, I do it with

HIDK1_Send(MODIFERKEYS_NONE, KEY_PRINTSCREEN);
HIDK1_Send(MODIFERKEYS_NONE, KEY_NONE); /* release key */

And if I need CTRL+ALT+DELETE, then it is

  HIDK1_Send(MODIFERKEYS_LEFT_CTRL|MODIFERKEYS_RIGHT_ALT, KEY_DELETE);
  HIDK1_Send(MODIFERKEYS_NONE, KEY_NONE); /* release key */

Pretty easy 🙂

The Application

Now I have everything in place. What I want is an application which sends the ‘Print Screen’ key event to the host with a single button press. For this I re-use the reset button of my FRDM-KL25Z (see this post how to do this). Then to add LEDs to show the status of the USB enumeration (see this post how to add LED’s). Adding some mechanics for LED blinking and button debouncing, and voilà:

  for(;;) {
    WAIT1_Waitms(10);
    cnt++;
    if (SW1_GetVal()==0) { /* button pressed */
      WAIT1_Waitms(100); /* wait for debouncing */
      if (SW1_GetVal()==0) { /* still pressed */
        /* send print screen */
        HIDK1_Send(MODIFERKEYS_NONE, KEY_PRINTSCREEN);
        HIDK1_Send(MODIFERKEYS_NONE, KEY_NONE); /* release key */
      }
      while(SW1_GetVal()==0) {} /* wait until button is released */
    }
    if (HIDK1_App_Task()==ERR_OK) { /* run the USB application task: this will send the buffer */
      if ((cnt%100)==0) {
        LEDR_Off();
        LEDG_Neg(); /* blink green LED if connected */
      }
    } else {
      if ((cnt%200)==0) {
        LEDG_Off();
        LEDR_Neg(); /* blink red LED if not connected */
      }
    }
  }

The red RGB LED blinks if not connected to the host, and the green one if it is connected as USB HID Keyboard. And when I press the reset button, it sends the ‘Print Screen’ key to the host. Finally I can do ‘print screen’ with my left hand while having my right hand on the (FRDM-KL25Z) keyboard :mrgeen:

Summary

With the creation of the USB HID Keyboard Device Processor Expert component, I have turned my FRDM-KL25Z init a generic USB keyboard device. With a simple button press I can send any keyboard actions to my laptop, making such as ‘print screen’ a single button press again. So it makes it a great device even for users with disabilities. And my ‘disability’ is solved now too: I get a print screen with the press of single button :mrgreen: :

Using FRDM-KL25Z as USB Keyboard Device

Using FRDM-KL25Z as USB Keyboard Device

Other ideas I have in mind: I thinking of turning the FRDM board into a wireless presenter. All what I need on top of this is either Bluetooth (but this would need to be a Bluetooth HID Keyboard), or maybe just using two FRDM boards: one is the keyboard dongle, and the other is sending data over the wireless communication channels. Still thinking about the best solution, and ideas are welcome.

The project created above (along with the components) are as always available on GitHub. While it does not support international keyboard mappings, that project has been very useful for me. I hope it is useful for you too.

Happy Keyboarding 🙂

97 thoughts on “Using the FRDM-KL25Z as USB Keyboard

  1. Hi Erich,

    I was going through your “Keypad using FRDMKL25Z post”, well that a great work. i too surely like such shortcut to press a key and print a screen to pdf.

    i was having a idea, can we club this project with the accel_senser on the board and make a mouse which can move in open space. can you guide me … to make it… i think, like keyboard, the USB HID mouse do have the same descriptor or the report to send.

    Like

    • Yes, I had the same idea. I’m right now working on the HID Mouse device support, so hopefully I can publish this soon. Then it should be easy for you to add the accelerometer to it so you have an ‘air’ mouse.

      Like

  2. Pingback: Using the FRDM-KL25Z as a USB Mouse Device | MCU on Eclipse

  3. Can’t seem to get the ring buffers to initialize.
    FSL_USB_HID_Keyboard_Device Component does not generate the ring buffer component. So I can proceed further. Any ideas?

    Like

  4. Pingback: USB for the Freescale ARM Kinetis KL46Z and K21D50M | MCU on Eclipse

  5. Hi. I tried the USB HID example on the Freescale USB stack and it works great, but the code is very difficult to follow for me. So I wanted to try this example, but I can’t make it work.
    I’m trying to use this example with a JM60, but I have a couple of problems.
    First, in Init_USB, under settings in USB RAM address, the value is 1860, but it says that the memory is used for the CPU component. So I went to the CPU component and disabled Memory Area 3. This solved this problem. Is this config ok?
    Second, if I compile the project I get this error:

    Generated_Code\usb_dci.c:907:error:C1815 handle not declared (or typename)

    Apparently “handle” is not defined anywhere. What I have to do to solve this problem?
    Thanks

    Like

  6. Hello Erich, I loaded the project into Eclipse 10.5, along with your excellent set of components from GitHub. Initially I had the program cycle through USB_ISR just after _EI(). To make it work, in USB0:Init_USB_OTG I disabled Asynchronous Resume interrupt, set up pull-up control to OTG/SW and enabled D+ pullup for USB-FS. Hope this helps.

    PS. I am connecting a vintage computer keyboard I scored at a flea market to a PC with USB.

    Like

    • Hi Witek,
      hmm, strange. I have had not do do such a thing when I connected the board to my DELL laptop. As for the pull-up selection: I made recently an improvement in this area as the Freescale stack had its own settings. With the new component it is possible to select the proper settings in the USB Init component, and then they are not overwritten by the software stack.

      Like

  7. Hello…
    I finished this example with free error before debug it… while debugging a problem occurred appear a message: Error creating session: The connection chosen in the Launch Configuration Main Tab is unknown.

    what should I do?

    Like

  8. Hello Eric, I put your USB_CDC program example in my FRDM Board and it run perfectly, but, when I put the HID_KeyBoard code example in my FRDM Board, this don’t run, can you help me please?

    Like

  9. Hi
    i’m trying to achieve the clicking action(right and left click) on TSS switch at the same time .If you can plz guide how to do it will be very helpful for my university project.

    Like

  10. Hi Eric ,
    Its really a great project & can we use it as a keyboard along with usb mouse . Could u give me the values for keystrokes like (Alt+F4 , Wind+L , Force shutdown ,) and from where do i get to know these values .

    Like

    • What you are asking for is a combined USB device (mouse plus keyboard). You could do that, but I have not done it myself. As for the key stroke values, see the HIDK1.h in the generated code folder.

      Like

  11. Hey eric ,
    Im trying to implement your code , where should i write the desired input(i.e i need to run CTRL+ALT+DEL with my reset button ) ..when i have edited in H1K1 in send() its not allowing me to edit .
    where should i write the led code,,
    Please help me ,,

    Like

  12. thanjs to ur reply
    Hey Eric ,
    I am trying to implement ur project on FRDM-KL25 , but my FRDM is opening as bootloader rather than FRDM-KL25 , so i am unable to transfer the .hex file on to flash . I have tried copying .sda’s and debug sdas on my bootloader window , but still no success.
    Please help thanx in advance

    Like

  13. For the component part, my software gives a warning ‘Cannot create FSL_USB_Keyboard_Device inherited component/template. Component not installed/supported’
    The Processor Expert Component PEupd files on github cannot be found. Therefore, we were unable to install the update for the project. What should we do?

    Like

  14. Hi, Erich

    I’m trying to configure and initialize the USB HID on MCF51JM128 using your HID and USB_Stack beans, but I still haven’t got successfull. I got an error after compiling: “Illegal 16-bit small data area relative reference to symbol g_Mem’ from address 0x00001DB4 in section ‘.text’ of file ‘usb_dci_c.obj ‘. This type of reference has a range from 4294934528 to 32767 bytes.”

    I know this is because I didn’t declare this memory section in linker command file (.lcf), but I’m not sure exactly how and at what point to do it.

    Could you help me please?

    Thanks a lot.

    Best regards,

    Marco Coelho
    Applications Engineer
    Siletec Eletronica

    Like

    • Hi Marco,
      you need to tell the compiler that it shall use the far (32bit) data model, so it uses 32bit addresses for data. Go to the project settings, ColdFire compiler options, Processor Settings and set the Data Model to ‘Far (32bit)’. This should solve your problem.

      Like

      • Hi, Erich

        I had already done this settings, but I still got the same errors. This memory section “g_Mem” is not declared in .lcf file. I suppose this is tha cause for that, but I don’t know how to declare it there. I also got a warning after compiling the code: “_g_Mem(.usb_bdt) in file usb_dci_c.obj is referenced but has not been written. Check your linker command file.” Thanks for your attention.

        Like

      • Hi Marco,
        yes, for the ColdFire compiler you need t do this in the linker file. See https://github.com/ErichStyger/mcuoneclipse/blob/master/Examples/DEMOJM/CDC_MCF51JM128/readme.txt as example.
        – Generate at least once code to have a linker file created.
        – Disable linker file generation: in CPU component > Build options set ‘Generate LCF file’ to ‘no’
        – add following block between .text:{} and .data:{}
        .usb_bdt :
        {
        . = ALIGN(512);
        __BDT_BASE = .;
        *(.usb_bdt)
        __BDT_END = .;
        } >> userram

        Like

  15. GUYS,
    I appreciate Your help.
    But im very very bad at KDS, so my another question:
    “SW1_GetVal()==0”
    what is the SW1? i do not get why it is not just HIDk1. Please explain me this 🙂

    Like

  16. Yea now i get why it did not work :0 im tottaly dumb… 😀 Thank You for all Your answers. As my defensive line i can use fact that its is my first (hardware/software)project with KL25z. Im student of 2nd year on IT department. And rather preffer high level apps than things like this one 🙂
    Greetings!

    Like

  17. Hi Erich
    Have you ever tried to make a project with Kinetis and bidirectional HID? I think a HID device class with that capability will be great! Some hint about?
    Regards,
    Mauricio

    Like

    • Hi Mauricio,
      no, I have not done that. But creating a generic HID device is something on my list for a long time. I just have ordered two books from the library about USB yesterday to learn more about this topic. But no idea if I ever will find the time to do it. Or have you done something you can share?

      Like

  18. Hello Erich,

    Seams that locks from keyboard (Num. Caps, & Scroll) are not supported in host mode. I am using latest usb stack, v5.0. Is there a fix for this?

    Like

  19. Hi Erich,
    I’m a new KDS user and I’ve managed to get a K22FN working as a HID keyboard using your Pex Components. Do you know where I might start if I needed to add Volume Up/Down buttons to the keyboard also? Thanks!

    Like

      • I’ve tried sending the codes (KEY_VOLUME_UP & KEY_VOLUME_DOWN) using the HIDK1_Send method but Windows does not read them. However the alphanumeric key codes and modifiers work fine.

        I’ve been doing some research and it looks like the multimedia keys require another interface (consumer control HID) to work. Do you think I could get this to work by making a copy of the FSL_USB_Stack component that can have 2 interfaces – HID_keyboard and HID consumer? I’m just now working out how the USB Stack components work 🙂

        Thanks!

        Like

      • Hi Michael,
        if you have enough USB protocol knowledge, it should not be too hard to extend the USB CDC with an additional combined interface. But I have not looked at the consumer control HID class myself yet.

        Like

  20. Hi Erich been trying to follow your tutorial here. However I am having issues when I get to the part of looking at the functions: App_Task(),SendStr() , SendChar(), etc. For some reason it tells me “code for method has not been generated yet.”

    I am also getting a bunch of “error in the inherited componet settings (CDC) (USBINIT) etc

    Would you happen to know how to fix this issues?

    Like

    • Hi Vildic,
      have you already created code with Processor Expert? Right click on the .pe file in the project, and then use ‘Generate Processor Expert Code’.
      I hope this helps,
      Erich

      Like

  21. Hey Erich great turorial, I got it all to work. Is it possible to add more buttons using the Pins on the board? So for instance the reset button is now acting as Prt Sc, if i connect a button using the one of the pins how i can i then assign it to to act as the ENTER key.

    Like

      • Thanks for your response Erich.

        I have already connected the button to a breadboard and into the pin on the KL25z. What components or what files do I need to modify to add a new button?

        Like

      • Hi Erich I looked at the pin Tutorial. Right now I have a button in a breadboard, that is connected to Pin PTE1. I went to the PIN for I/O in SW1:BitIO and changed PTA20/Reset_b to PTE1. I reprogrammed the board but when I press the button on the breadboard it doesn’t do the function its set to. Do you have any thought as to why that could be happening?

        Like

      • Hi Jorge,
        Not sure if I can follow. Are you using a pull-up or pull-down for your button? If you have configured the reset pin to use PTE1, then your board will reset if you press that button.
        If you want to use a button as keyboard input, you need to define a dedicated pin (with pull-up, for example) and use that, and don’t mix it up with the reset functionality.
        I hope this helps,
        Erich

        Like

      • I am using a button very much like the one on an xbox controller, or PS4. I want to assign a keyboard key to that button. I thought that by changing the pin of sw1 to pte1 it would do that.

        Like

      • Hi Jorge,
        the pin needs to have a defined logic level if the push button is not closed. For this you need to add either an internal or external (usually pull-up) resistor.
        I hope this helps,
        Erich

        Like

      • Hi Erich,

        Thanks so much for all your help. I got my buttons to work. Right now I’m trying to implement a joystick in addition to my 4 buttons. What component from processor expert do I need for that?

        Thanks again for your help!

        Like

      • Hey Erich,

        Got everything to work thanks for your tutorials.

        My board sometimes does not initialize when I plug it in. When its plugged and ready it turns the LED green, sometimes i have to plug and unplug multiple times until it turns green, If it doesn’t turn green nothing works. Would you know anything that could cause that?

        Thanks

        Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s