Pic Lab, PIC18, Experiment #5, USB

When I started to dig into USB topic it was quite a surprise, the amount of efforts the smart people around the world put into it just unbelievable. Even more fascinating fact – it actually worked out. The excitement reached some saturation when I realized the prices (: Vendor ID will cost you 5 grands per year, want to use a USB logo – no problem, just add another 6 grands on the top. Now we could test the compliance of your device with our standard for n grands… The companies using USB are listed on the website www.usb.org (USB Implementers Forum – USB-IF). The curious one could just count a number of companies and calculate the profit just from the year subscription, and this thing is going on for a long long time already. Well they have definitely built an addictive stuff. I, personally, have found the usage for my projects a bit excessive, I definitely not ready to put 5 grands per year for USB in the cheap humidity sensor.

Now, there are tons of different information, it is really challenging to grasp all at once, I will be honest and won’t pretend that I got everything, have a lot of holes in the knowledge, I just need to make it alive and respond to my commands. So, what I gonna to do is to show the info which looked important to me and then will conduct two experiments on this “foundation”.

USB diagram

As I mentioned, all the information is actually gathered on the website www.usb.org, but I will summarize the pieces I thought are important for quick walk-in (mainly for me actually, as usually I write the article in a way I took the journey – from the very beginning to the end). Just going to warn you – if you want to have detailed basics, it is super stuffed with the information and it will take a good part of your life to get into all it.

One more thing to clarify – I will look only into USB 2.0, the speed of which is limited and equal to 480 Mb/s. USB 3.0 is going to be even more complicated and this not what I want to be dragged into.

Now the funny part, as I said the website www.usb.org has a lot of detailed information, but there are guys from Cypress which perhaps just as me were looking through all info and were overwhelmed with all the info striking their heads so they decided to shrink it and compose a narrowed down document written in a very good way, I ended up just in reading of their manual.

General description

Take a look at the picture in the post beginning – any system with the USB connection could be represented as host – device pair. For our convenience – we wont be looking to the complicated cases with multi-hosts, or hubs (this is a whole another story) and so on. So, the system might have one host and many devices (127 max maybe if I remember correct).

Again, back to the very beginning, think about the system as about the host-device pair and don’t think about other cases. The next abstraction level is that the data going in-out by special channels (pipes). Those pipes entering/exiting to/from special connections points – they called endpoints. There is always at least one endpoint and at least one pipe, used for the configuration, command and so on. This pipe is bidirectional and connected to to the endpoint0. Other pipes are data channels and they are unidirectional – either into the device, either out from the device. Each single device has an unique address assigned to it by the host during the connection.

Hardware part

Остальные трубы это каналы данных, они однонаправленные – или в девайс или из него. Каждый девайс имеет определенный адрес, который назначается ему хостом при подключении девайса, а дальше по этому адресу хост коннектится к девайсу-эндпоинту.

Хард часть

a usb cable

A USB cable consists of 4 wires: the ground, the supply and the differential data pair. By the way, the peculiar thing about the usb – the supply voltage is 5V, but in the same time the data has the 3.3V high voltage level.

Next, there is a quite useful picture of different port types (which I copied from the Cypress website):

usb connector types

Also the pinout from the same source:

USB pinout

USB devices use special pull-up resistors, which help the host to determine the proper speed supported by the device:

pull-up resistors determine the supported speed by device

The full speed mode is settled when we have D+ pull-up, the low speed one is when D- is pulled up. In the full speed mode the device will start from the specific sequence to determine whether the host actually supports such a speed. If it does not then pull up resistor is being removed. One more thing – if there is no pull-up resistor at all, the host will reject all the communications.

From the power supply perspective, want to remind to all that 500mA is a maximum current for USB 2.0. Also, there are 3 classes of devices: high consumption (500mA), low consumption (100mA) and with built-in power supply. All classes must consume up to 100mA max on a startup , then the command SET_CONFIGURATION initialized and the device can be configure to use 500mA. Not going to describe hybrid situation, it is kind of a way of the scope.

Another thing – the maximum cable length supported by USB-IF is 5 meters for full speed, and the diffpair should be twisted, while for the low speed it is limited by 3 meters (?) and no need to twist the diffpair. I wanted to mention this because there are tons of places which sell extra long cables without repeaters, they are counterfeit.


USB uses NRZI + Bit stuffing.

The clean NRZI – 0 is a state change, 1 no changes in the state:

NRZI modulation

Bit stuffing is just adding of 0 if there were already 7 consecutive 1s. This made for still having the ability to synchronize the communication in a right way (if there would be a lot of consecutive 1s the CDR will not have enough of transitions and fail to generate a proper clock).

One more important thing – LSB goes first, MSB the last. So, the 11dec will look like – 11010000.

USB uses special states for the communication:

  • Differential 0/ Differential 1 – the most logical and simple states, diff 0 is when D- is high and D+ is low. 1 is opposite
  • J state/ K state – depends on the speed mode, for a full speed a J state is diff 1, K state is diff 0. For the low speed mode the situation is opposite
  • SE0 – single ended 0, both wires are low. Means either reset, either disconnected device, either end of package (EOP)
  • SE1 – single ended 1 – both wires are high, this is an exception, if you see it – something is wrong
  • Idle – the event before sending of packages, or after, or … just an idle state, nothing is happening. But it depends on the speed as well (since speed is determined by the pull-up)
  • Resume – used to wake-up the device out of the sleeping mode, initialized by K-state
  • Start of packet (SOP) – used before any package is going to be sent, characterized by transition from the Idle state to the K-state
  • End of packet (EOP) – the end of transmission. SE0 is initiated and holds for minimum two bits, after it the J state is initiated for at least one bit time
  • Reset – SE0 holds at least 10ms
  • Keep alive – used only in the low speed mode, EOP being sent each 1 ms to keep the device from entering to the sleep mode

For simplicity and convenience I took the table from USB-IF (www.usb.org):

USB states

USB speeds

As I mentioned, USB 2.0 has 3 speed modes:

  • low speed devices – 1.5 Mb/s
  • full speed devices – 12 Mb/s
  • high speed devices – 480 Mb/s

Now lets talk about the reference frequency – it is obvious that at the highest speed we would have low speed operational as well, but the price for it is the increased power consumption and the extra noise.

The list of okay ref frequencies (remember, I am just an enthusiast, not a professional in the field):

  • 6 MHz – for low speed
  • 12 MHz – full speed, 4 times multiplier
  • 24 MHz – high speed devices with x20 multiplier

By the way, many of microcontrollers of Microchip family with an USB support, support the Active Clock Tuning technology – it uses the host to adjust the clock to the required accuracy level, thus the necessity of the external USB reference clock is eliminated.

Processes during USB device connection (dynamic detection/enumeration/configuration)

Initially there is a dynamic detection stage:

  1. As soon as device is connected, it can consume 100mA max
  2. Then a host will determine whether a connected device has a right type at all (remember, pull-up resistors)
  3. Enumeration – host understood that a new device is being connected and it request more information about it (GET_PORT_STATUS)
  4. The host determine the speed, initially just either low speed either full speed (the second GET_PORT_STATUS request)
  5. The reset is being initialized for a new connected device
  6. During the reset, the host determines whether the device support the high speed (special KJ patters), I don’t fully understand this part and don’t bother to check
  7. Then host determines if device is still in the reset mode, if it is then host will wait till the device exit. When it is not in the reset anymore, the host can communicate with the device by address 00h – a default address for any new connected device. This means that hosts can’t determine new connected devices simultaneously, it has to go one by one.
  8. The host begin to learn more info about the device. The command GET_DESCRIPTOR is sent and the device send descriptors back
  9. The device is getting the address (SET_ADDRESS), now a whole communication will be performed by this address
  10. Configuration stage – again GET_DESCRIPTOR is sent, and now, the device throw all descriptors to the host
  11. The host search for drivers
  12. The specific configuration for the device is proceeded
  13. The device is configured

USB descriptors

In the previous section the bullet #10 has mentioned descriptors, those are placeholders for all the configurations and all the required info about specific operation modes. Again, stealing the picture from Cypress website:

Cypress picture, explaining descriptors structure

It shows different configurations, the device may have either one configuration and one interface, either one configuration and two interfaces (for instance, USB headset might have one interface for audio and another one for the control).

The root of the diagram is a Device descriptor:

0bLength1The descriptor length (18 bytes)
1bDescriptorType1descriptor type – DEVICE (01h)
2bcdUSB2USB revision (for 2.0 it will be 0x0200)
4bDeviceClass1class of the device
6bDeviceProtocol1device protocol
7bMaxPacketSize01Maximum packet size of endpoint0 (8/16/32/64 bytes)
8idVendor2Vendor id (VID) prepare your money
10idProduct2Product id (PID) defined by the developer
12bcdDevice2release version
14iManufacturer1manufacturer index
15iProduct1index to the info about the product
16iSerialNumber1serial number
17bNumConfiguration1the number of supported configst

Then we have the Configuration Descriptor, here we have the info about the amount of interfaces, modes of current consumption for example.

0bLength1The length of descriptor (9 bytes)
1bDescriptorType1type of the descriptor – CONFIGURATION (02h)
2wTotalLength2total length (?)
4bNumInterfaces1the number of interfaces in this configuration
5bConfigurationValue1the value for this configuration (to be selected with SET_CONFIGURATION)
6iConfiguration1link to the configuration description
7bmAttributes1modes of current consumption
8bMaxPower1Max allowed consumption current (a step is 2mA)

Then we have the interface, which includes the number of endpoints and the device class.

0bLength1The descriptor length
1bDescriptorType1type of the descriptor – INTERFACE (04h)
2bInterfaceNumber1zero based index of the interface (?)
3bAlternateSetting1Alternative settings (?)
4bNumEndpoints1Number of endpoints (not including the endpoint0)
5bInterfaceClass1The interface class
6bInterfaceSubClass1The subclass of the interface
7bInterfaceProtocol1The interface protocol
8iInterface1The link to description of the interface

Now, the descriptor of the endpoint

0bLength1The length of descriptor (7 bytes)
1bDescriptorType1The descriptor type – ENDPOINT (05h)
2bEndpointAddress1bit 3..0: address, bit 6..4 – reserved, 0es, bit 7 direction (0 OUT)
3bmAttributes1The transmission type,the sync type, the endpoint type
4wMaxPacketSize2Maximum packet size
6bInterval1An interval in miliseconds

In the end of this section I have to mention the configuration of the device class

The choice of a proper class is extremely important task, since it will determine does your device need a separate driver or not, many operational systems have native drivers already included, simplifying the designer life.

00hDevicew/o specificationThe device without any class, the interface descriptor determines required driver
01hInterfaceAudioThe speaker, the mic, the sound card
02hDevice + interfaceCommunication + CDC controlA modem, a wifi adapter
03hInterfaceHuman Interface Devicce (HID)keyboard
05hInterfacePhysical Interface Device (PID)Joystick with a feedback
06hInterfaceImage operationcamera, scanner
07hInterfacePrinterPrinter, cnc
08hInterfaceMass Storageflash drives
09hDeviceUsb hubhubs
0AhInterfaceCDC-DataNot quite sure why we need another one, but it can be used only after confirmation on USB-IF
0BhInterfaceSmart cardcard readers
0DhInterfaceSecurity devicefingerprint scanner
0FhInterfacePersonal medical deviceheart rate monitor
DChDevice+interfaceUSB diagnostic deviceDevice used by USB-IF for the certification purposes
E0hInterfaceWireless controllerBluetooth adapter
EfhDevice + interfaceMisc
FEhInterfaceSpecial applicationIrDa bridge
FFhDevice+interfaceVendor specificAn indicator of special vendor driver requirement

More info of course on the USB-IF website.

My scope of interests is just two classes – HID (03h) and CDC (02h). The first one is a really convenient one, no need other drivers at all – but there is a bit BUT – you must get VID/PID = pay the subscription. Well, I dont want to pay, but at least I will experiment with it.

The second one might be used for the series port emulation, so software is really simple from a host side and the migration from typical uart application should be quite easy. Also, starting from windows 10 the CDC class drivers are native supported.

USB communication

The last section of boring theory.

Stealing one more time the picture from Cypress website. USB operates with “frames”:

usb frames (from the Cypress website)

Each frame consists of the starting packet SOF, transaction or transactions. Transaction is consists of subpackets, each of them start from a special SYNC pattern:

sync pattern

…and ending by the EOP pattern. A transaction should has at least one token packet and optional data packets and handshakes (ack).

Each packet can be represented as:

PID – Packet ID, packet type – IN/OUT/SETUP/SOF

ADDR – optional, the device address

EP – the endpoint number

Payload data – an optional data (0 .. 1023 bytes)

CRC – an optional CRC check

All fields except PID are optional. Tokens determine the direction and the purpose and always initialized by the host.

SOF used for timing frame marks (again the Cypress manual a quite handy):

tokens timing

1 ms is a full speed timing, for the high speed this time is equal to 125 us.

After tokens we have data packets, their composition looks like this:

data packet composition

DATA1 toggled by DATA0 and vice versa, it serves as an additional monitor which seek if transmission is right.

The last part of the packet is a handshake, which can be different:

ACK – transfer is successful

NACK – transfer is failed

STALL – the transmission has an error, initiated by the device

NYET – device is not ready yet for a next packet (only for high speed device)

If host send the data, then handshakes are sent by the device. If the device is transmitting the data, then handshake come from the host.

All right, enough of this business, lets try to some exciting work.

Experiment: With help of CDC/HID class blink LED and then try something more serious, for instance, get the data from the ADC.

What do we have: PIC18f14k50, a copy of Pickit3, the salea logic analyzer on it’s way.

Let me repeat myself – to workout whole USB from a scratch is extremely large task, really really big. You really should be brave if you decide to develop the stack by yourself, I am certainly not.

Almost all articles I was able to find were written in a next way – download the MLA lib from microchip website or use the fashion tool MCC (MPLAB code configurator), which I don’t like for some reason.

Beginning from the MLA – downloading the lib, opening the project from examples folder. Initially I took that one:


…and immediately having a trouble – there are tons of different libs, headers, files I have no idea for. How to transfer it in a right way to my project and not miss any important link? Well you have to package it right inside of Mplabx:

packaging the project inside of MplabX

This command will pack the whole project to a single archive;

Now unpack it, change the device on PiC1814K50 and getting it … to the another fail. 4 errors, well maybe different uC should have different config (most likely) and probably there are other things, lets explore all errors.

Error 1 – S2_PORT originally connected to RB2, but there is no such pin in PIC18F14K50, changing it to RB6.

Error 2 – LEDs are connected to the Port E, there is no such port in our uC.

Error 3 – ADCONbits register, there is no such a register, for simplicity I just switched off all analog functions ANSEL = 0; ANSELH = 0;

Error 4 – our uC has a different PLL configuration. We can control PLL in software only when HSINTOSC = 8MHz in PIC18f14K50, but such configuration is forbidden for usb module by the datasheet. Going to the system.c file and adding such thing:

#pragma config FOSC = HS 
#pragma config PLLEN = 1

Also switch off all unneeded configs there.

OK, now it complied without problems, initially we try out in proteus, appeared they do support virtual usb port.

simulation of the virtual USB host in proteus

I have noticed one bad thing about this code for this uC – the code has occupied 67%, this is a lot, almost no space left for main functions…

OK, lets try proteus further – make the LED to blink in the beginning, and when the button is pressed it will increase the ascii code +1 an return the sum back, i.e. if 1 is pressed it will respond with 2, if a is pressed it will return b and so on.

proteus simulation result

It works, wow, I did not have much of the hope to be honest.

Tried to launch in the real chip – of course it failed, the led was constantly blinking, messages about the maximum current can not be supported, eh, sure thing it never works from the beginning.

…5 days (actually evenings) passed, well I definitely made myself busy for a strange reason.

Now the summary time, about all the mistakes I made:

  1. I took the project PIC17F46.. from the MLA library, since I thought that there is no such example project with my uC on the board. As usual, the life by itself made me to read more datasheets and more theory, and I found different variants of devboards used in example projects. When I started to search specifically devboards and found that there is actuall one devboard with PIC18F14K50 on board. It costs 50 bucks and it already ancient, they still have max232 IC on it (!). It called low pin count development board. During digging in MLA examples I found the cdc_basic folder in which I saw low_pin_count_usb_development_kit_pic18f14k50.x. How did I miss it initially…
  2. Chinese counterfeit parts, how many times I said to myself “never more”, but did the same mistake again since price was so low, I bought this “wonderful board”
Chinese devboard …

The idea is great for sure – we have the usb port, a couple of buttons, a potentiometer connected to the ADC and two LEDs. The dream comes true, all I needed to experiment with for such a low price. Now it is a place where my doubts begin to develop: this devboard costs 3$, but digikey sells pic18f14k50 for 2.4$, this math clearly shows that something is wrong here. Well my suspicion is usual – aliexpress has tons of counterfeits, in a fact I doubt that there something original at all. And how many times I regretted I bought some IC there which failed quite fast…

After struggling with a code and the project I started to dig into PCB and this I figured out was really surprising. After poking it with a multimeter I had realized that vdd doesn’t have connection with vusb. Vdd was just hanging in the air – the microcontroller get the supply thru the protection ESD diodes, wow just wow. I wrote the letter to the seller, explained that pcb is a complete mess and asked how it can be done like that. He answered that this is not possible, they have all kind of checks passed and so on and so forth, then they finally send to me the pcb schematic and layout. There I had another miraculous time – from usb port vdd we have a narrow wire, small via, another narrow wire and another small via and only then uC. Who the hell makes the supply routing like that? Well, even in such config there should be electrical contact – but there is no such a via on the real pcb, even no photos on aliexpress, there is no such a via. I wrote to the seller again, explained the whole situation again, showed some proofs of my theory – the answer, oh sorry you are are right, we had some defective pcbs in our stocks and you got the bad one accidentally. But no any further reaction, no refunds no sending of the good one, really another bad experience and days of my wasted time. Repeating one more time for myself – never ever I buy the active device on aliexpress/ebay or whatever website which can be blamed in distributing of counterfeits.

3. The Microchip company. I don’t know they strategy, but they are not really so user friendly and the software is far away from being without bugs. After a long pause I realized that pickit2 simple firmware writer software doesn’t work anymore and now there is a hex loader inside of Mplab X, it is called Mplab X IPE. To have the ability to tune some knobs, one need to go to the advanced mode and use the password microchip. Looks overcomplicated but workable at first glance, but when real chip was connected thru pickit3 the super duper soft has all fields disappeared. Eh, googling again, it is a known problem and resolve it you need to disconnect the pickit then put required setups, then connect it again. Wtf, seriously this is the way software going now?

4. Myself. I have got the code from PIC18F14K50, I wanted to know why my previous code did not work. As appeared, from all existing projects I picked the one which has the completely different chip structure. I needed to add a special file fixed_address_memory.h, which has pointers to the certain USB RAM area for PIC18F14K50 and without them it is not possible to make this code to work.

Let me proof that I got everything to work:

The buttons being pressed are -1, 2, 3 and RC5. OK, that’s a relief, now I have the experiment point to which I can return in case everything’ll go wrong. Now we could poke real device to learn the stuff and maybe try to reduce the occupied memory.

An interesting note: the latest current mla lib (at the moment of this article writing it was May 2020, as appeared not a bad year at all, after what 21 and 22 “gifted” to us…) used the interruptions, but the previous year one used the polling method.

Кстати, интересная ремарка – последняя текущая версия mla либ (на данный момент май 2020 года) использует прерывания, в то время когда прошлогодние версии используют polling метод, где просто с определенным интервалом запускаются usb таски.

Starting from descriptors, they are stored in usb_descriptoprs file.

Initially we have the device descriptor, where VID/PID and sereial number are being determined, kind of simple stuff, then we have the configuration descriptor:

/* Configuration Descriptor */
    0x09,//sizeof(USB_CFG_DSC),    // Size of this descriptor in bytes
    USB_DESCRIPTOR_CONFIGURATION,                // CONFIGURATION descriptor type
    67,0,                   // Total length of data for this cfg
    2,                      // Number of interfaces in this cfg
    1,                      // Index value of this configuration
    0,                      // Configuration string index
    _DEFAULT | _SELF,               // Attributes, see usb_device.h
    50,                     // Max power consumption (2X mA)

From this part we can understand next interesting things – we have two interfaces, the config index value is 1, device has own supply (which actually is not my case) and the current limited by 100mA.

Changing the file immediately to match our needs.

Then we have the interface 0 descriptor:

9,//sizeof(USB_INTF_DSC),   // Size of this descriptor in bytes
    USB_DESCRIPTOR_INTERFACE,               // INTERFACE descriptor type
    0,                      // Interface Number
    0,                      // Alternate Setting Number
    1,                      // Number of endpoints in this intf
    COMM_INTF,              // Class code
    ABSTRACT_CONTROL_MODEL, // Subclass code
    V25TER,                 // Protocol code
    0,                      // Interface string index

Kind of usual stuff, the same class as ours – CDC 0x02.

Then we have the endpoint (num, the direction and the transmission type)

    USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
    _EP01_IN,            //EndpointAddress
    _INTERRUPT,                       //Attributes
    0x0A,0x00,                  //size
    0x02,                       //Interval

Then we have a second interface and two endpoints, as I understood it used directly for the data transmission (TX/RX):

9,//sizeof(USB_INTF_DSC),   // Size of this descriptor in bytes
    USB_DESCRIPTOR_INTERFACE,               // INTERFACE descriptor type
    1,                      // Interface Number
    0,                      // Alternate Setting Number
    2,                      // Number of endpoints in this intf
    DATA_INTF,              // Class code
    0,                      // Subclass code
    NO_PROTOCOL,            // Protocol code
    0,                      // Interface string index
    /* Endpoint Descriptor */
    USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
    _EP02_OUT,            //EndpointAddress
    _BULK,                       //Attributes
    0x40,0x00,                  //size
    0x00,                       //Interval

    /* Endpoint Descriptor */
    USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
    _EP02_IN,            //EndpointAddress
    _BULK,                       //Attributes
    0x40,0x00,                  //size
    0x00,                       //Interval

One endpoint for the data output, another one for the input. Then we have constant strings giving more device info (which will be visible in the operation system).

The next file is usb_config.h – a bit simpler here, just USB setups, speed, buffers, pullups and the operation type (POLLING/INTERRUPT).

Then we can start to look into usb_device.c. Here we have USB SENSE check which is setup for the supply type, also number of configs, then next important functions:

USBDeviceInit() – Initialization of USB -> interrupts are disabled, interrupts registers are cleared, endpoint register is cleared, the configuration defined in usb_config.h loaded, address is resetted to 0x00. State to DETACHED_STATE, and many many more.

USBDeviceTasks() – the main stack function for the device. Polling method calls it constantly to check. So will try to consider it with more attention.So if device has ATTACHED_STATE, we have a next routine:

  • If there is no SE0 yet, then put the reset, enable reset interrupts and the device status changed to POWERED_STATE
  • If there was ActivityIF interrupt, then the wake up routine is initiated USBWakeFromSuspend()
  • If there is a reset interrupt, then again USBDeviceInit()
  • If we got IDLEIF then the enter to sleep procedure is called
  • Start of Frame interrupt is proceeded here as well
  • StallIF interrupt is proceeded
  • Error interrupt handled
  • The end of transmission handled

USBConfigureEndpoint() – I did not get it fully, beside that fact that endpoint direction is determinedфиг пойми эту функцию, понятно что она определяет направление эндпоинта, внутрянка сходу непонятна

USBEnableEndpoint() – enabling of the certain endpoint and calling of the previous procedure

USBDeviceDetach() – software device disconnection from a bus

USBDeviceAttach() – the software connection to a bus, change of the state, config setup, enabling of interrupts

A couple of shady for me functions about the transfer control

USBCtrlTrfTxService(), USBCtrlTrfRxService()  

USBStdGetDscHandler() – GET_DESCRIPTOR request handler. The more I read the more I amused how much work put in the usb stack 🙂

USBStdGetStatusHandler() – GET_STATUS request handler

Then tons of functions about endpoints, entering to sleep, exiting from sleep and one small interesting function:

USBGet1msTickCount() – it shows the working time in miliseconds from point of USBDeviceInit() called. For 8 bits controllers this function doesn’t work in a sleep mode, while for 16 bits it does.

OK, enough about this file, now switching to other files – system.c. A small file, but quite important one: we have config here, System_initialize() – which dedicated to buttons and leds for our case and interrupt handler, which just hit USBDeviceTasks() anytime any interrupt happened (not just USBIF).

Then usb_device_cdc.c – a lot of unclear procedures, but I still stop at some moments which will hopefully help me to understand more about CDC uart.

getsUSBUSART(uint8_t *buffer, uint8_t len) – returning the data from USB buffers, will be used for reading of the data obviously. If there is no data return 0.

putUSBUSART(uint8_t *data, uint8_t  length) – transmit of one symbol

putsUSBUSART(char *data) – string transmission

putrsUSBUSART(const char *data) – transmit string from a program memory, can just put quotemarks and your text

CDCTxService(void) – preparation of the UART, should be called at least once

And finally the app_device_cdc_basic.c

APP_DeviceCDCBasicDemoInitialize() – initialization with serial port parameters.

APP_DeviceCDCBasicDemoTasks() – Main demo function, the button handler, throwing away the prepared messaged and also adding +1 to ASCII symbol and putting it to the terminal.

That are all functions I’m going to review, just want to mention again – there is no function to transfer the number to the terminal using smallest amount of memory (one byte) instead you can use the string transmission, which is obviously far from the optimal. So I have added my personal as usual:

void NumToUART(uint16_t number)
	uint16_t bignum = 10000; //max divider for uint16
    uint16_t temp_Number = 0;
	uint8_t numtemp = 5;    //an amount of digits (max)
    static uint8_t temp_data[5];

	while (numtemp>0)       //checking how many digits number has
        if (number/bignum)
        bignum = bignum/10;

   for (uint8_t i = numtemp; i>0; i--)    
       if (i == numtemp)
            temp_data[numtemp-i]=number/bignum - (number/bignum/10)*10;   
           bignum = bignum/10;
    for (uint8_t i =0; i<numtemp; i++)
        if( USBUSARTIsTxTrfReady() == true)


I changed the code a bit, added 12345 transmission by symbol ‘p’, the result is here:

quick test

Initially I pressed 1, 2, 3 then Enter, then the button on the devboard, all worked fine.

Then I thought that I could download the usb data logger and show step by step processes happening inside. But then I realized that this type of software is overcomplicated again and it did not add the desire to learn additional stuff about them, so I just gonna show the configuration output from one of them:

I did not pack everything, just main things – descriptor of the device, configs, interfaces and endpoints. This software in principle shows it more or least conveniently, but if one will try to follow the data transfer – it is simply overwhelming. Also, up to this point I strongly decided that pic18f14k50 is not a good choice at all, with simple CDC class it is already 70% of the memory occupied.

OK, moving further, the one thing left to try – to add a simple analog to digital conversion and transfer the value over usb by button pressed event.

Settling the ADC:

 TRISB4 = 1;
    ANSEL = 0;
    ANSELH = 4;
    ADCON0 = 0b00101000;
    ADCON2 = 0b10000100;
    ADON = 1;

Nothing special here, you can check the previous experiment about ADC – just setup of the certain channel as an input.

In the cdc_basic code I have added a special flag by ‘p’ button:

 if((numBytesRead > 0)&&(num_flag))
            /* After processing all of the received data, we need to send out
             * the "echo" data now.
            GO = 1;
            data_int = (ADRESH<<8) | ADRESL;

Without interrupts, just for a quick check:

the experiment with ADC

The reference level is vdd, 5.06V, then 1 lsb = 4.95mV, 144*4.95 = 712.8mV.. Eh..

Tried a couple things, never succeed, was completely drained and sick of this board anyway, so no debug for this stuff and no more freaking burning of the time for non original ICs.

At this moment we shall conclude that CDC function realization is successful but have really not too much sense with a given cost (at least for me). HID is going to be a different story, maybe from a scientific reasons it would be interesting to study it and try it, but I see no future for it for my current “home” applications, so will not bother to dive deep into it.

The sources

Две вещи которые я для себя вынес из этого эксперимента:

A couple of lessons learned:

1. No more counterfeit ICs, it has too much consequences

2. PIC18F14K50 is a cool chip, but if you have a free XC compiler it is really hard to use for something else beside the USB stack, I’d recommend to look to something else.

3. If all you need from USB is just an uart operation – think twice before you jump into USB business, I’d rather just go the simple uart path and ftdi converter

Leave a Reply

Your email address will not be published.