logo elektroda
logo elektroda
X
logo elektroda

PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard

p.kaczmarek2 1005 2

TL;DR

  • Builds USB HID support for the PIC16LF1459 using the free SDCC compiler, then turns it into LED control, a calculator, a mouse, and a keyboard.
  • Uses the PIC’s built-in USB module, internal oscillator, and Active Clock Tuning, with custom HID descriptors and EP0 SET_REPORT handling instead of external libraries.
  • The chip offers 14 KB of Flash, 1 KB of RAM, and hardware USB up to 12 Mbps through a 512-byte shared memory buffer.
  • A Python HID script can switch the LED, exchange 2+2-style messages, and send mouse and keyboard reports successfully.
  • The hardest parts are descriptor setup and basic USB state handling; the keyboard also needs a HID_SET_PROTOCOL response or the host may reject it.
Generated by the language model.
ADVERTISEMENT
This content has been translated flag-pl » flag-en View the original version here
📢 Listen (AI):
  • PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    I invite you to a little adventure with the PIC16LF1459, an 8-bit microcontroller made by Microchip, distinguished by its hardware USB support with a minimum of external components. You don't even need an external oscillator - the built-in one has enough precision. Here I will run USB communication on it using the free SDCC compiler, all without external libraries. I will show here examples of HID applications, such as simple LED control, data exchange using a simple 'maths coprocessor' as an example, and then show how to realise practical HID-compatible devices - a mouse and a keyboard.


    A proper tutorial on SDCC:
    Tutorial PIC18F2550 + SDCC - Part 1 - Setting up the operating environment

    A few words about the PIC16LF1459
    The PIC16(L)F1459 is an 8-bit microcontroller based on a RISC core by Microchip. It has 14 KB of Flash memory and 1 KB of RAM. Depending on the version, it can operate at 1.8 - 3.6 V (L version), and at 2.3 to 5.5 V. It is distinguished by hardware USB (compatible with USB Low and Full Speed - up to 12 Mbps) based on a 512-byte shared memory buffer. Among other things, the PIC16LF1459 is available in a hobbyist-friendly DIP package.
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard


    Connecting to the PIC
    The PIC shown requires a very small number of components to run. Basically you need to give a 100 nF decoupling capacitor between the power supply pins, this is for the principle, as in any microcontroller, and a 10 kΩ resistor between the MCLR pin and the power supply - this to get the PIC to boot, shorting MCLR to ground resets the circuit.
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    The PICKIT3 is suitable for programming, although with the new Device File even the PICKIT2 can handle it. We program via ICSP:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    I posted the Device File for PICKIT2 in my older topic about this PIC. There I was still running it with a commercial microC, for that reason I think the solution presented today is a big step forward.
    PIC16F1459 boot board with USB
    PICKIT2 handled this PIC with no problems at my place:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard

    Hello World - Blink LED
    Blink LED is often the first program I run on microcontrollers. It allows me to check that the batch starts at all and that it works at least somewhat stably.
    Code: C / C++
    Log in, to see the code

    The program starts the PIC with the built-in resonator, the OSCCON register is used to configure it. I then configure the entire C port as an output, and flip its state to the opposite state in a loop. The function waiting 1000 ms is a simple blocking loop. In the target program this shouldn't be there, as it just wastes CPU cycles, but for a test it's acceptable.
    Now just connect the LED and resistor to any pin from port C:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    Basically that's it - the LED flashes:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard


    Simple USB - LED control via HID
    Now we come to the actual USB part. I'll start with the LED control. I'll put all the code in the appendices, although I won't go quite into the theory that is "under the hood". Still first the connection - I used the USB breakout board for this:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    Overall, running USB on the PIC16LF1459 in the SDCC compiler requires proper configuration of the registers responsible for this module and defining basic descriptors for the device, configuration and interface in the HID class. Hardware-wise, the PIC relieves us to a large extent, e.g. with the Active Clock Tuning (ACT) function, which, through continuous synchronisation to USB frames (SOF), allows a stable 48 MHz from the built-in internal oscillator, but we have to program the descriptors ourselves. In the C code itself, handling the diode control is reduced to intercepting SET_REPORT requests on the control endpoint (EP0) and reading the sent data byte. To connect to such a configured device on the computer side, we can use a short script in the system Python based on the HID library, which, due to the use of the standard HID class, works "out of the box" and does not require the installation of any additional drivers in the operating system.
    The most important parts - the descriptors, let's pay attention to Vendor and Product, by this we will know our device:
    Code: C / C++
    Log in, to see the code

    Transaction handling - state machine. The user is basically concerned here with parsing ep0out_buf, which is the HID frame buffer, it can contain more than one byte. We have simplified it for now.
    Code: C / C++
    Log in, to see the code

    Hardware-wise we still need to remember the extra capacitor on Vusb (depending on the PIC version):
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    In the LF version we connect it to the power supply, otherwise a capacitor is needed.
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    This is enough for the PC to see the PIC:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    Now the PC program - for convenience in Python. A short script that finds the device by the entered Vendor/Product and sends this one byte over the HID to switch the LED:
    Code: Python
    Log in, to see the code

    The program works and the LED can be controlled.
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard


    Packages USB HID - calculator
    Let's further demonstrate for completeness the sending of a byte string. We can base this on the calculator example, the script sends a 2+2 style request and the PIC calculates this equation and returns the result. Such a mechanism requires two-way communication - the PIC has to receive the data, process it and then send back the result. A one-way message is not enough here, as with LED control.
    So, receiving the data - processing it will be realised by the processIncoming function:
    Code: C / C++
    Log in, to see the code

    Sending the response - with the readiness flag attached (the program will then detect it and send the data on):
    Code: C / C++
    Log in, to see the code

    The heart of the calculator, i.e. parsing and calculations - the least important here, but for reference:
    Code: C / C++
    Log in, to see the code




    Result:

    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard


    USB HID device - mouse
    A standard USB mouse is an HID device these days. This means that the code from the previous example, with very little change, can control the movement of the cursor on the computer screen. The first thing that is missing to fully simulate a mouse is device identification - the device must report in the descriptors as a mouse. You can at least look up the ID values in the documentation at Microsoft:
    https://learn.microsoft.com/en-us/windows-har...dclass-hardware-ids-for-top-level-collections
    I have therefore completed the identifiers:
    Code: C / C++
    Log in, to see the code

    Code: C / C++
    Log in, to see the code

    Windows has since recognised the PIC as a mouse:

    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard
    Now the report still has to be sent. Windows rigidly expects the mouse to report, among other things, two bytes representing the cursor offset. All you need to do is send this via the HID, as in the previous example. For the example, I send the data resulting in a circular movement of the cursor. I didn't want to count trigonometry, so I generated an array resulting in circular movement:
    Code: C / C++
    Log in, to see the code

    Sending reports - looped index:
    Code: C / C++
    Log in, to see the code

    Generation of HID packet for mouse - format as expected by computer.
    Code: C / C++
    Log in, to see the code

    The result - the mouse does circles under any system, practical use is not there, but you can make a joke for someone.
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard


    USB HID device - keyboard
    Similarly - we look at the documentation and change the descriptors, the format of the data to be sent. In the HID Report Descriptor I changed the Usage Page to 0x01 (Generic Desktop) and Usage to 0x06 (Keyboard):
    https://learn.microsoft.com/en-us/windows-har...dclass-hardware-ids-for-top-level-collections
    In the Interface Descriptor I changed the protocol byte from 0x02 (Mouse) to 0x01 (Keyboard) - this tells the host at the interface level which device it is dealing with.
    The format of the report also changed - the mouse used to send 3 bytes (buttons, deltaX, deltaY), the keyboard sends 8 bytes in the standard Boot Keyboard format:
    Code: C / C++
    Log in, to see the code

    Apart from this, under the hood I had to do a small fix - as the HID support is done without an external library, the response to the HID_SET_PROTOCOL request (0x08) was missing. The host sends this request to the boot protocol keypads to switch between boot and report mode - without a valid response (ZLP), the host could declare the device inoperable.
    This leaves the main loop where I send the message:
    Code: C / C++
    Log in, to see the code

    Result:
    PIC16LF1459 tutorial - USB HID support in the free SDCC compiler - LED, mouse and keyboard

    Summary
    In summary, even a microcontroller as small as the PIC16LF1459 allows you to run functional USB communication without the use of heavy external libraries. Thanks to the built-in USB module, the ability to work with an internal oscillator and the Active Clock Tuning mechanism, it is possible to build a working device with a minimum of external components. The examples shown - from simple LED control, to data exchange in the form of a simple 'maths coprocessor', to mouse and keyboard emulation - show that implementing the HID class is relatively affordable, even when you implement most of the protocol handling yourself in code.
    At the same time, it can be seen that the most work is required to prepare the correct descriptors and to handle the basic states of the USB protocol. Once this foundation is correctly implemented, however, further experimentation mainly boils down to processing the transmitted data and defining custom HID reports. This opens the way to building all sorts of simple USB peripherals - controllers, service tools or interfaces to communicate with your own projects.
    Of course, you could also make a gamepad or joystick with multiple buttons and axes - maybe that's your next project?
    The whole thing also shows that the free SDCC compiler is completely sufficient for working with this microcontroller and avoids closed, commercial environments. So, with a bit of patience, it is possible to create a fully working USB stack 'from scratch' and, in the process, get a good understanding of what actually happens under the hood of USB communication. This gives you a lot of freedom in designing your own HID devices and the ability to tailor the implementation exactly to the needs of the project.
    Do you use HID communication in your own projects?
    I attach the developed codes together with the compiled hex files.
    Attachments:
    • pic16lf1459_usb_mouse_jiggler.zip (59.65 KB) You must be logged in to download this attachment.
    • pic16lf1459_usb_led.zip (63.18 KB) You must be logged in to download this attachment.
    • pic16lf1459_usb_keyboard_typer.zip (71.73 KB) You must be logged in to download this attachment.
    • pic16lf1459_usb_calculator.zip (74.79 KB) You must be logged in to download this attachment.

    Cool? Ranking DIY
    Helpful post? Buy me a coffee.
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    p.kaczmarek2 wrote 14459 posts with rating 12468, helped 650 times. Been with us since 2014 year.
  • ADVERTISEMENT
  • #2 21887250
    Damian_Max
    Level 21  
    Posts: 393
    Help: 40
    Rate: 96
    p.kaczmarek2 wrote:
    Result - the mouse does circles under any system, practical use it has no use, but you can make someone a joke.

    Commercially it's called a 'mouse simulator / mouse jiggler', it more-or-less randomly moves the cursor so the computer doesn't go into sleep mode (which, depending on usage, can be more-or-less ethical xD).

    All in all, you could make a UART→keyboard converter. Maybe it wouldn't have some huge uptake, but you can think of a few applications: without installing any drivers, you could turn any computer into a uart logger. By adding a pre-prepared web page, entering full-screen mode, you can have an application with one-way communication with the microcontroller. You can even connect this via an adapter to a phone, I think it would even work with an iPhone.
  • #3 21887263
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14459
    Help: 650
    Rate: 12468
    I've done something similar before and it worked in practice. That is, transferring keys from one computer to another:
    Wireless virtual HID keyboard for the other computer on WiFi

    Old times, still on RN131 and PIC18F45K50 modules.

    By the way, you could do the same for your phone - sometimes I need to run something on Android, and Android also supports keyboard and mouse (via USB OTG), so you could potentially share your computer's keyboard and mouse with Android
    Helpful post? Buy me a coffee.
📢 Listen (AI):

FAQ

TL;DR: With 14 KB Flash and 1 KB RAM, the PIC16LF1459 can run USB HID from the free SDCC compiler using only core register setup and hand-written descriptors. "The free SDCC compiler is completely sufficient," the tutorial argues, for developers who want LED, mouse, and keyboard HID examples without external USB libraries. [#21884259]

Why it matters: This shows you can build a practical USB device on an 8-bit PIC with minimal hardware, no paid toolchain, and native HID compatibility on a PC.

Approach Cost / lock-in USB HID control Learning value Notes
SDCC + custom USB code Free, open workflow Full manual control Highest Requires writing descriptors and EP0 handling
mikroC + library-style workflow Commercial / closed Easier start Lower protocol visibility Mentioned as the older approach
PICKIT3 Standard programmer Yes Practical Used directly for ICSP
PICKIT2 + Device File Older tool Yes Practical Works after adding the device file

Key insight: The hardest part is not blinking LEDs or sending reports. The real work is correct USB descriptors and endpoint state handling; once that foundation works, mouse, keyboard, and custom HID devices become straightforward.

Quick Facts

  • PIC16(L)F1459 is an 8-bit Microchip MCU with 14 KB Flash, 1 KB RAM, and hardware USB using a 512-byte shared memory buffer. [#21884259]
  • The LF version operates at 1.8–3.6 V; the non-L version operates at 2.3–5.5 V. USB supports Low Speed and Full Speed up to 12 Mbps. [#21884259]
  • Minimal bring-up parts are a 100 nF decoupling capacitor and a 10 kΩ pull-up from MCLR to supply; shorting MCLR to ground resets the chip. [#21884259]
  • The tutorial configures the internal oscillator through OSCCON = 0x36 for a simple LED test, then relies on USB clock tuning for stable USB operation. [#21884259]
  • A standard boot keyboard report uses 8 bytes, while the mouse example sends 3 data bytes: buttons, X delta, and Y delta. [#21884259]

How do I get USB HID working on a PIC16LF1459 with the free SDCC compiler and no external USB libraries?

Set up the PIC16LF1459 USB registers, write the USB descriptors yourself, and handle HID control traffic on endpoint 0. The tutorial shows LED control, a calculator, a mouse, and a keyboard without external USB libraries. Start with device, configuration, interface, and HID report descriptors, then add a transaction handler that parses EP0 SETUP and OUT packets. This works because the MCU already has hardware USB support and can use its internal clock with USB synchronization. [#21884259]

What minimal external components are needed to run a PIC16LF1459 with USB, including MCLR, decoupling, and VUSB connections?

You need very little hardware. Use a 100 nF decoupling capacitor across the supply pins and a 10 kΩ resistor from MCLR to VDD so the PIC boots normally. Shorting MCLR to ground resets the MCU. For VUSB, the tutorial notes an extra capacitor may be required depending on the PIC version; on the LF version, VUSB is connected to the supply, while other versions need the capacitor arrangement shown in the thread. [#21884259]

Why is the internal oscillator on the PIC16LF1459 accurate enough for USB, and how does Active Clock Tuning work?

It is accurate enough because the chip continuously corrects its internal clock against USB traffic. The tutorial uses Active Clock Tuning (ACT), which locks timing to USB SOF frames and lets the PIC derive a stable 48 MHz USB clock from the built-in oscillator. That removes the need for an external oscillator in this design. This matters because USB timing is strict, and poor clock accuracy causes enumeration or transfer failures. [#21884259]

How do I wire and program a PIC16LF1459 over ICSP using PICKIT3 or PICKIT2?

Program it over standard ICSP with PICKIT3, or use PICKIT2 if you add the correct Device File. The tutorial states both programmers worked with this PIC in practice. 1. Wire the ICSP lines from the programmer to the PIC. 2. Add the 10 kΩ MCLR pull-up and normal power connections. 3. Flash the firmware, then test with a simple LED blink before enabling USB. That sequence verifies the board, programmer, and configuration bits first. [#21884259]

What is USB HID, and why does it let a PIC16LF1459 work on a PC without installing extra drivers?

USB HID is a standard device class for human-interface style devices such as keyboards, mice, and simple control interfaces. HID works here without extra drivers because the PC already includes native support for that class. The tutorial uses HID for LED control and data exchange, then extends the same base to mouse and keyboard emulation. On the PC side, even a short Python script can open the device by VID 0x04D8 and PID 0x1459 and send reports directly. [#21884259]

How do I write the USB device, configuration, interface, and HID report descriptors for a PIC16LF1459 HID project?

Write them explicitly in C arrays and match them to the device you want the host to see. The device descriptor in the tutorial sets bcdUSB 2.00, idVendor 0x04D8, idProduct 0x1459, and one configuration. The interface and HID report descriptors then define whether the host treats the PIC as a generic HID device, a mouse, or a keyboard. If those descriptors disagree with your report format, enumeration may succeed but behavior will still be wrong. [#21884259]

Why does Windows detect a PIC16LF1459 as a mouse or keyboard only after changing the HID descriptors and protocol bytes?

Windows decides what the device is from the descriptors, not from your application code. In the tutorial, the interface descriptor changes to class 0x03, subclass 0x01, and protocol 0x02 for a mouse or 0x01 for a keyboard. The HID report descriptor must also switch its Usage and packet structure to match. Until both parts match, Windows will not bind the PIC as the intended boot mouse or boot keyboard. [#21884259]

What is the HID_SET_PROTOCOL request, and why does a custom PIC keyboard firmware need to answer it with a valid ZLP?

HID_SET_PROTOCOL is a host request that switches a boot keyboard between boot and report protocol modes. The tutorial found that a custom keyboard firmware must answer this request with a valid ZLP, or the host may mark the device unusable. "HID_SET_PROTOCOL" is a USB HID control request that selects the report-handling mode, a key requirement for boot-class keyboards during enumeration and normal host negotiation. If you skip it, the keyboard can enumerate yet still fail in real use. [#21884259]

How can I control an LED from a PC over USB HID on a PIC16LF1459 using a simple Python hid script?

Open the HID device from Python, then send a one-byte payload that the PIC reads on endpoint 0. The tutorial script uses hid.device(), opens VID 0x04D8 and PID 0x1459, and writes [0x00, 0x01] for LED on or [0x00, 0x00] for LED off. On the firmware side, the PIC reads ep0out_buf[0] and sets or clears LATC bit 0. This gives you direct PC-to-board control without a custom driver. [#21884259]

What is the role of SET_REPORT on endpoint 0 in a PIC16LF1459 USB HID implementation?

SET_REPORT carries host data to the PIC over the HID control path, usually through endpoint 0. In the tutorial, endpoint 0 OUT receives the report data, the firmware checks whether it is waiting for OUT data, and then parses ep0out_buf. That is how the LED example gets its control byte. "SET_REPORT" is a HID class request that sends application data from host to device, using a standard control-transfer path rather than a vendor-specific driver. [#21884259]

How do I send and receive multi-byte HID packets on a PIC16LF1459 for a simple calculator or data exchange example?

Receive the host packet on EP0, process the byte string, then queue a reply buffer for the IN direction. The calculator example parses expressions like "2+2", computes the result, and copies the response into reply_buf through sendHIDReply(). The firmware sets a ready flag so the USB layer knows to transmit the prepared packet. This turns the PIC into a tiny HID data processor, not just a one-way control target. [#21884259]

Why does my custom PIC16LF1459 USB HID device enumerate but fail to behave correctly when sending mouse or keyboard reports?

Enumeration only proves the host accepted your descriptors; it does not prove your report format is correct. The tutorial shows two common failure points: wrong mouse or keyboard descriptors, and missing control-request handling such as HID_SET_PROTOCOL. A boot mouse expects 3 data bytes in the shown example, while a boot keyboard expects 8 bytes. If packet size, Usage, protocol byte, or timing is wrong, the device appears in Windows but misbehaves. [#21884259]

PIC16LF1459 with SDCC vs mikroC for USB HID projects — which approach is better for learning and flexibility?

SDCC is better if you want control, portability, and a deeper understanding of USB HID internals. The tutorial explicitly calls the SDCC solution a "big step forward" from the earlier commercial mikroC-based setup. With SDCC, you avoid a closed environment and learn descriptors, endpoint state machines, and report handling directly. mikroC may reduce early effort, but SDCC gives more flexibility for custom HID designs and protocol-level debugging. [#21884259]

What report format does a boot mouse or boot keyboard expect, and how do I build those packets correctly on a PIC16LF1459?

A boot mouse needs button and movement fields, while a boot keyboard uses the standard 8-byte boot report. In the mouse example, the PIC sends 3 bytes: buttons, X delta, and Y delta. In the keyboard example, the firmware sends modifier plus keycode, then an empty release report after a short delay. 1. Build the packet in the exact order the host expects. 2. Copy it into the endpoint buffer. 3. Hand ownership to USB hardware with the proper data-toggle handling. [#21884259]

How could I extend this PIC16LF1459 USB HID tutorial into a gamepad, joystick, or other custom USB peripheral?

Reuse the same USB foundation and change the HID report descriptor plus application logic. The tutorial says that once descriptors and USB state handling are correct, further work mainly becomes parsing or generating your own report data. That makes gamepads, joysticks, controllers, service tools, and project-specific interfaces realistic next steps. Add buttons, axes, or custom fields to the report, then map your GPIO or sensor data into those bytes. [#21884259]
Generated by the language model.
ADVERTISEMENT