Activating a toggle LED using Hasu's TMK code.

Moobimon

15 Sep 2015, 04:12

Hi guys,

Me and 4 other buddies grouped up to each make our own custom mechanical keyboard. This is my 1st time doing a thing of the sort, and I haven't even looked into mechanical keyboards before this project.

So after a few months of planning and work, I have finally soldered everything together (to a teensy, following Matt3o the great's guide, building brownfox). I continued using his guide to programme the teensy, using Hasu's tmk, and using the g60 as a base model.
Link to Matt3o's guide I used to programme:
http://deskthority.net/workshop-f7/how- ... t7177.html

It all works. In fact, I am typing this post at the moment with this keyboard I built :D.
But, there is a little thing I want to add to the keyboard. I have a toggling FN layer, and in order to know if this layer is toggled ON or OFF, I connected an LED to the FN key, and connected it to the GND and D4 pin on the teensy. Everything works, I connected it to the VCC and GND pins to check.
Now. To the point. I have searched the interwebs, and found nothing. Can any saint tell me how to activate the LED when press the FN key, and deactivate it when it is pressed again? (Basically outputting to a pin when the Fn is pressed)

Thanks a whole lot to any helper :D.

User avatar
Ray

15 Sep 2015, 10:35

There is no standard way of doing this in tmk. And there are many ways to skin this cat.

keyboard.c contains the following:

Code: Select all

 // update LED
    if (led_status != host_keyboard_leds()) {
        led_status = host_keyboard_leds();
        keyboard_set_leds(led_status);
    }
where keyboard_set_leds(...) calls led_set(...), which is used for setting/clearing the outputs for the lock-leds.

A quick way to achieve your goal is to remove the if in the snippet above and change led.c in the gh60 folder to set the output depending on the layer_state (more on that: https://github.com/tmk/tmk_keyboard/blo ... /keymap.md)

User avatar
flabbergast

15 Sep 2015, 11:02

Here's another way (there's a pull request about this on github: https://github.com/tmk/tmk_keyboard/pull/198 )
Namely: edit tmk_core/common/action_layer.c like this:
- line 6, add

Code: Select all

#include "led.h"
- line 66 (between 'layer_state = state;' and 'layer_debug(); dprintln();') add

Code: Select all

led_layer_set(state);
edit tmk_core/common/led.h, add

Code: Select all

void led_layer_set(uint32_t state);
on line 36 (after the declaration of 'led_set' function)

Then you can edit the file 'led.c' in your keyboard's directory, define a 'led_layer_set' function, for instance like this:

Code: Select all

void led_layer_set(uint32_t state) {
    DDRD |= (1<<4);

    /* Led for Layer 2 */
    if ((1<<2 & state) != 0) {
        PORTD |= (1<<4);
    } else {
        PORTD &= ~(1<<4);
    }
}
This function gets passed the state of layers, so you can act on this in whatever way you like. The code above should turn on a LED on PD4 whenever layer 2 is active.

User avatar
HzFaq

15 Sep 2015, 11:36

Nice, I'll be borrowing this for sure, thanks :D.

Moobimon

15 Sep 2015, 23:21

Thank you so much for all the instant and awesomely helpful replies! Its so amazing to have such an amazing community.
I opted to use flabbergast's method, and it worked like a charm. Thank you again so so much to both of your answers :D.

Anyway, on a different note, I know that it doesn't have anything to do with this topic, I was wandering how do i make macro keys work, and if any of your guys could help with that (if it is not against forum rules to ask for help on a different topic to the post's original one).

User avatar
Ray

16 Sep 2015, 09:24

https://github.com/tmk/tmk_keyboard/blo ... /keymap.md
2.3 has some (little) explanation of how tmk's built in macro support. With keyboard/hhkb/keymap.c it isn't too hard to figure it out.

User avatar
flabbergast

16 Sep 2015, 10:56

This. Also have a look at keyboard/hhkb/keymap_hasu.c, he's got a couple of macro examples. However there's no "on-the-fly" macro programming in TMK (yet?), and each macro consumes one "Fn" action, so I think there's a limit of 16 for all the "Fn" things, including layer switching, etc...

User avatar
Ray

16 Sep 2015, 11:07

it is 32 actually. That limit is still easily reachable, depending on what you want the keyboard for.

Tomer

12 Nov 2015, 21:58

flabbergast wrote: Here's another way (there's a pull request about this on github: https://github.com/tmk/tmk_keyboard/pull/198 )
Namely: edit tmk_core/common/action_layer.c like this:
- line 6, add

Code: Select all

#include "led.h"
- line 66 (between 'layer_state = state;' and 'layer_debug(); dprintln();') add

Code: Select all

led_layer_set(state);
edit tmk_core/common/led.h, add

Code: Select all

void led_layer_set(uint32_t state);
on line 36 (after the declaration of 'led_set' function)

Then you can edit the file 'led.c' in your keyboard's directory, define a 'led_layer_set' function, for instance like this:

Code: Select all

void led_layer_set(uint32_t state) {
    DDRD |= (1<<4);

    /* Led for Layer 2 */
    if ((1<<2 & state) != 0) {
        PORTD |= (1<<4);
    } else {
        PORTD &= ~(1<<4);
    }
}
This function gets passed the state of layers, so you can act on this in whatever way you like. The code above should turn on a LED on PD4 whenever layer 2 is active.
Hey, I tried using this function for more than one FN key, but it just wouldn't compile...
Would you by any chance know how to do that? I have two FN keys and a CAPS lock key which I want to have toggle LEDs when they are "activated".

Here's my function for the two FN keys (I don't know how to make one for the caps lock key):

Code: Select all

void led_layer_set(uint32_t state) {
    DDRC |= (1<<7);

    /* Led for Layer 1 */
    if ((1<<1 & state) != 0) {
        PORTC |= (1<<7);
    } else {
        PORTC &= ~(1<<7);
    }
}

void led_layer_set(uint32_t state) {
    DDRD |= (1<<5);

    /* Led for Layer 2 */
    if ((1<<2 & state) != 0) {
        PORTD |= (1<<5);
    } else {
        PORTD &= ~(1<<5);
    }
}


And here's the compile error:

Code: Select all

Microsoft Windows [Version 10.0.10240]
(c) 2015 Microsoft Corporation. All rights reserved.

C:\Users\kagan>cd C:\Users\kagan\Documents\Keyboard Software\tmk_keyboard-master\keyboard\gh60

C:\Users\kagan\Documents\Keyboard Software\tmk_keyboard-master\keyboard\gh60>make -f Makefile.pjrc
/usr/bin/sh: dfu-programmer: command not found
/usr/bin/sh: dfu-programmer: command not found

-------- begin --------
avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Size before:
   text    data     bss     dec     hex filename
  22546      24     148   22718    58be gh60_pjrc.elf


mkdir -p obj_gh60_pjrc
Compiling C: led.c
avr-gcc -c -mmcu=atmega32u4 -gdwarf-2 -DF_CPU=16000000UL -DBOOTLOADER_SIZE=4096 -DPROTOCOL_PJRC -DBOOTMAGIC_ENABLE -DMOUSEKEY_ENABLE -DMOUSE_ENABLE -DEXTRAKEY_ENABLE -DCONSOLE_ENABLE -DCOMMAND_ENABLE -DNKRO_ENABLE -DSLEEP_LED_ENABLE -DNO_SUSPEND_POWER_DOWN -DVERSION=unknown -Os -funsigned-char -funsigned-bitfields -ffunction-sections -fdata-sections -fno-inline-small-functions -fpack-struct -fshort-enums -fno-strict-aliasing -Wall -Wstrict-prototypes -Wa,-adhlns=obj_gh60_pjrc/led.lst -I. -I../../tmk_core -I../../tmk_core/protocol/pjrc -I../../tmk_core/common -std=gnu99 -include config.h -MMD -MP -MF .dep/obj_gh60_pjrc_led.o.d  led.c -o obj_gh60_pjrc/led.o
led.c:46: error: redefinition of 'led_layer_set'
led.c:35: error: previous definition of 'led_layer_set' was here
make: *** [obj_gh60_pjrc/led.o] Error 1

C:\Users\kagan\Documents\Keyboard Software\tmk_keyboard-master\keyboard\gh60>

User avatar
flabbergast

12 Nov 2015, 23:40

In C, each function has to have a unique name - your code defines 2 functions with the same name. So, you'll need to combine the two LEDs into one function:

Code: Select all

void led_layer_set(uint32_t state) {
    DDRC |= (1<<7);
    DDRD |= (1<<5);

    /* Led for Layer 1 */
    if ((1<<1 & state) != 0) {
        PORTC |= (1<<7);
    } else {
        PORTC &= ~(1<<7);
    }

    /* Led for Layer 2 */
    if ((1<<2 & state) != 0) {
        PORTD |= (1<<5);
    } else {
        PORTD &= ~(1<<5);
    }
}

Tomer

13 Nov 2015, 09:17

flabbergast wrote: In C, each function has to have a unique name - your code defines 2 functions with the same name. So, you'll need to combine the two LEDs into one function:

Code: Select all

void led_layer_set(uint32_t state) {
    DDRC |= (1<<7);
    DDRD |= (1<<5);

    /* Led for Layer 1 */
    if ((1<<1 & state) != 0) {
        PORTC |= (1<<7);
    } else {
        PORTC &= ~(1<<7);
    }

    /* Led for Layer 2 */
    if ((1<<2 & state) != 0) {
        PORTD |= (1<<5);
    } else {
        PORTD &= ~(1<<5);
    }
}
Awesome! It worked! Thanks a bunch flabbergast!
Would you by any chance know how to make the function for the caps lock key?

User avatar
flabbergast

13 Nov 2015, 10:12

Caps lock led is normally set in led.c, in the led_set function, just like in other keyboards (see e.g. gh60 example).

The point is that the "toggled" led indicators (caps, num, scroll locks) are dealt with in the led_set function, while the "layer" leds in the led_layer_set function.

Tomer

13 Nov 2015, 10:42

flabbergast wrote: Caps lock led is normally set in led.c, in the led_set function, just like in other keyboards (see e.g. gh60 example).

The point is that the "toggled" led indicators (caps, num, scroll locks) are dealt with in the led_set function, while the "layer" leds in the led_layer_set function.
I tried using the function for the CAPS lock key which LED is connected to the D6 pin:

Code: Select all

void led_set(uint8_t usb_led) {

    DDRD |= (1<<6);

    /* Led for CAPS */
    if ((1<<1 & state) != 0) {
        PORTD |= (1<<6);
    } else {
        PORTD &= ~(1<<6);
    }
	
}
But now it won't compile:

Code: Select all

Microsoft Windows [Version 10.0.10240]
(c) 2015 Microsoft Corporation. All rights reserved.

C:\Users\kagan>cd C:\Users\kagan\Documents\Keyboard Software\tmk_keyboard-master\keyboard\gh60

C:\Users\kagan\Documents\Keyboard Software\tmk_keyboard-master\keyboard\gh60>make -f Makefile.pjrc
/usr/bin/sh: dfu-programmer: command not found
/usr/bin/sh: dfu-programmer: command not found

-------- begin --------
avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Size before:
   text    data     bss     dec     hex filename
  22558      24     148   22730    58ca gh60_pjrc.elf


mkdir -p obj_gh60_pjrc
Compiling C: led.c
avr-gcc -c -mmcu=atmega32u4 -gdwarf-2 -DF_CPU=16000000UL -DBOOTLOADER_SIZE=4096 -DPROTOCOL_PJRC -DBOOTMAGIC_ENABLE -DMOUSEKEY_ENABLE -DMOUSE_ENABLE -DEXTRAKEY_ENABLE -DCONSOLE_ENABLE -DCOMMAND_ENABLE -DNKRO_ENABLE -DSLEEP_LED_ENABLE -DNO_SUSPEND_POWER_DOWN -DVERSION=unknown -Os -funsigned-char -funsigned-bitfields -ffunction-sections -fdata-sections -fno-inline-small-functions -fpack-struct -fshort-enums -fno-strict-aliasing -Wall -Wstrict-prototypes -Wa,-adhlns=obj_gh60_pjrc/led.lst -I. -I../../tmk_core -I../../tmk_core/protocol/pjrc -I../../tmk_core/common -std=gnu99 -include config.h -MMD -MP -MF .dep/obj_gh60_pjrc_led.o.d  led.c -o obj_gh60_pjrc/led.o
led.c: In function 'led_set':
led.c:27: error: 'state' undeclared (first use in this function)
led.c:27: error: (Each undeclared identifier is reported only once
led.c:27: error: for each function it appears in.)
make: *** [obj_gh60_pjrc/led.o] Error 1

C:\Users\kagan\Documents\Keyboard Software\tmk_keyboard-master\keyboard\gh60>

User avatar
flabbergast

13 Nov 2015, 12:06

Modified from gh60 to use pin D6:

Code: Select all

void led_set(uint8_t usb_led)
{
    if (usb_led & (1<<USB_LED_CAPS_LOCK)) {
        // output high
        DDRD |= (1<<6);
        PORTD |= (1<<6);
    } else {
        // Hi-Z
        DDRD &= ~(1<<6);
        PORTD &= ~(1<<6);
    }
}

Tomer

13 Nov 2015, 13:51

flabbergast wrote: Modified from gh60 to use pin D6:

Code: Select all

void led_set(uint8_t usb_led)
{
    if (usb_led & (1<<USB_LED_CAPS_LOCK)) {
        // output high
        DDRD |= (1<<6);
        PORTD |= (1<<6);
    } else {
        // Hi-Z
        DDRD &= ~(1<<6);
        PORTD &= ~(1<<6);
    }
}
Tried using this function but it didn't work when I turned on the caps lock key.
Then I tried changing the target LED to a functioning LED in case the problem was a faulty LED - no luck...

Any idea of what might be the problem?

User avatar
flabbergast

13 Nov 2015, 15:31

You may have the LED wired the other way, i.e. pin --- LED --- resistor --- 5V, rather than pin --- LED --- resistor --- GND (as assumed for the above code). If that's the case, you need to change the first branch of the if statement to

Code: Select all

DDRD |= (1<<6);
PORTD &= ~(1<<6);
Other than this, I can't really tell without having more information.

Tomer

13 Nov 2015, 16:30

flabbergast wrote: You may have the LED wired the other way, i.e. pin --- LED --- resistor --- 5V, rather than pin --- LED --- resistor --- GND (as assumed for the above code). If that's the case, you need to change the first branch of the if statement to

Code: Select all

DDRD |= (1<<6);
PORTD &= ~(1<<6);
Other than this, I can't really tell without having more information.
You were right! Thanks!!!

Post Reply

Return to “Workshop”