Tuesday, July 12, 2016

USB Serial Keyboard/Mouse

Previously, we made a PS2 keyboard emulator.  Here we are going to create a USB keyboard that makes easy to control another PC from a laptop.  (Alternatively, we can turn a tablet into a keyboard/mouse, that's a project for another time.) We'll use the USB serial port from the laptop to transmitt keystrokes to a microcontroller that provides the USB keyboard device interface.  The microcontroller module we choose is the TI's EK-TM4C123GXL LaunchPad.  It features the TI's Tiva series microcontroller with an 80MHz Cortex M4F core.  The Cortex M4F adds more computational capabilities to Cortex M3 with DSP and single precision floating number instructions.  The board actually has two Tiva M4C123 microcontrollers: one serves as the In-Circuit Debug Interface (ICDI) and a USB serial interface and the other is programmable and has a USB OTG/Host/Device port with a USB micro-A/B connector as well as the other pins broken out to the Launchpad BoosterPack XL expansion connectors: two 2x10 headers that include 35 signals and power/ground.  In addition, there are two pushbutton switches and 3-color LEDs.  The board cost is $12.99, a very capable board at a very attractive price.


TI Code Composer Studio (Ver 6.1.1) with TI ARM compiler 5.2.6 is used as the development tools and TivaWare C Series (2.1.2)  as the device library.  In the CCS, we create a new project by selecting the device TM4C123GH6PM, Stellaris In-Circuit Debug Interface and TI v5.2.6 compiler version.  The startup code and the linker commands are automatically generated.  The startup code sets up the interrupt vectors, including the reset handler which jumps to c_int00 to start the application.  The linker script sets up the memory allocation.  This device has 256K Flash and 32K SRAM.  At this point, we can make sure things are set up correctly by compiling the skeleton code.  We can also take a look at the default compiler options.

The first thing to add is the UART.  We simply borrow code from the examples in the TivaWare.  One of the interesting features of Tiva is that the TivaWare Peripheral Driver Library (DriverLib) code resides in the internal ROM.   These functions are prefixed with ROM_.   Also there are the MAP_ version of the functions that make it easy to switch between the ROM version and the Flash version. We have to define a compiler variable TARGET_IS_TM4C123_RB1 in order to resolve these functions (by mapping to ROM addresses).  Here RB1 refers to Silicon Revision B1; it is not clear how to tell the silicon revision.  If the flash version of the driver functions are used, driverlib.lib has to be linked in.  It is relatively easy to modify compilation options from the project properties.  As a debugging aid, we print out __DATE__ and __TIME__ macros so that we know if the microcontroller is flashed with the latest code.

Next is to initialize the USB in the device mode and set up as the USB HID device class.  Again we borrow from the examples.  Thanks to the usblib, it is fairly easy to set up the HID keyboard device.  Be sure to enable the USB peripheral and configure the pins and add USB0DeviceIntHandler to the interrupt vectors.  A callback function is defined to receive the events from the HID keyboard driver; the events includes connected/disconnected, suspend/resume, transmit complete.  Finally the key press and release are sent by calling the key state change function.  For starter, the ascii character received from the UART has to be translated into USB HID keyboard usage code and we have to transmit both key press and key release.  Only a subset of the usage code is defined in the TivaWare; the complete code can easily be found on the web.  Microsoft has a USB HID to PS/2 scan code translation table.  Once the USB is plugged in, to a computer, it is recognized as USB HID Keyboard and the key strokes are received as we type on a serial terminal.  I have to set the force device mode, otherwise it is not detected.  By default, the VBUS and ID pins are not monitored;   And as a side effect, the disconnected event is not reported in the mode.

Now we need to develop an application to transmit key press and key release in a raw form.  The USB keyboard reports up to 6 keys along with keyboard modifiers (CTRL/SHIFT/ALT/GUI).  We'll have to see if we need to send modifiers together with the keys or separately.  Also multiple bytes has to be sent, so we need a way to synchronize.  The serial protocol has to be a little more complex: three bytes may be needed, one for modifiers, one for key code and one to indicate key press or release.  Some unused bits may be used for sync.  Timing should also be used: the three bytes are transmitted together.  Tests indicate that the modifiers' states have to be transmitted with the keys, so the modifiers have to be maintained.  Also note that you may have to turn the legacy USB keyboard/mouse mode in the BIOS; that was the case for the GRUB running on one of my older computers.

A further modification is to turn it into a keyboard and mouse composite device.  We simply call the composite initialization functions, then we deal with two separate devices.   The mouse device reports the pointer delta movements and the button states.  Using Python tkinter, we bind ButtonPress, ButtonRelease and Motion events.  From the event coordinates, we calculate the delta movements.  Note that the deltas are signed 8-bit numbers.  In the Tk event, the left/middle/right buttons are numbered 1, 2 and 3 respectively.  But it appears that the middle and the right buttons are swapped at the receiving end.  It might be a configuration issue.  I tested it with both Linux and Windows; it worked fine.

In the future, more input devices can be integrated.

Another version is created using Cypress PSoC5LP kit CY8CKIT-059, a small $10 board, which is more convenient to carry.   The implementation effort is similar.  But I had to dig a little deeper into setting up the USB descriptors for a composite HID device and the endpoint programming.  Most of the USB device code is auto generated.

Tuesday, June 14, 2016

Power White LED Light Properly


The cheap LED light simply drives a parallel string of white LEDs with two or three batteries.  There is no other circuit elements, not even a current limiting resistor.  The most annoying problem is that the light gets dim fast as the battery voltage drops when there is still plenty of battery capacity left.  The proper way of doing it is a constant current boost converter,  LT1932 is such as an IC that comes in a small package, works down to 1V and is simple to hook up.  Here is the circuit diagram,

Here is the actual circuit board,


LT1932 can supply up to 40mA.  For higher current, we can take a boost regulator like LTC3429 (600mA 500KHz synchronous boost) and close the loop with current sensing.  The following circuit sources 120mA and works down to 1.8V.  It has about 90% efficiency.  When the input voltage is higher than the output voltage (such as using a Lithium Ion battery), LTC3429 acts like a low-dropout linear regulator.  


The actual circuit board,


Another incarnation with higher current (250mA) and in the size of a flashlight bulb (P13.5S),



Thursday, May 26, 2016

Headphone/Microphone Combo Jack

The notebook computers nowadays tend to have only one audio jack, a headphone and microphone combo.  It has four connections and requires a 4-ring 3.5mm plug.  The pinouts are as follows




1. Tip - Left audio
2. Ring 1 - Right audio
3. Ring 2 - Ground
4. Sleeve - Microphone

The measurements on a computer show that Pin 4 is +3.6V relative to Pin 3 and can supply 1.86mA (equivalently about 2KOhm pullup),  Pin 1 to Pin 3  and Pin 2 to Pin 3 are 32 Ohms (this is surprisingly low).  When the common earphone is plugged, the sleeve is shorted to the ground, and the earphone works normally.  But a normal microphone usually has only two connection, the tip and the sleeve, so it would not work.  A 4-ring splitter is needed.  Only one audio input channel (mono) is possible.  The voltage on the sleeve is to supply the FET amplifier in an electret condenser microphone.

The audio input and output are sometimes useful as a signal generator and a data acquisition channel.  The frequency response is usually 20Hz - 20KHz.   The output can be up to 24 bits at 192KHz and the input up to 24 bits at 96KHz.  They usually have excellent noise characteristics.

Here is a 1000Hz sine wave generated by the PC headphone output with 48KHz sample rate and captured in by a NI DAQ module at 100KHz sample rate.  All this can be done in Python, using the sounddevice and PyDAQmx modules.  The volume control affects the amplitude; +/-2V seems to be the range for my computer.


And 0.2Vpp 1KHz sine wave captured by the microphone at 48000 samples per second, 
The microphone input seems to saturate around 0.4Vpp.  And the input should be AC coupled.

Sunday, May 22, 2016

Ideal Transistor

The basic solid state amplification devices are bipolar, JFET and MOSFET transistors.  All of them can be considered as transconductance device, which is a current source controlled by a voltage.

A bipolar transistor has a diode input structure: the input voltage between the base and the emitter sees a small signal resistance of $ V_T/I_b $, where $ V_T= kT/q $ is the thermal voltage, about 26mV at the room temperature and the $I_b$ is the base bias current, usually in the 10$\mu$A range, so the resistance $r_b$ is about $2.6K \Omega$.   A voltage $v_{be}$, that generates $i_b = v_{be}/r_b$, would cause a collector-emitter current $i_c = \beta i_b = \beta v_{be}/r_b$; so the transconductance is $g_m = \beta/r_b$. With $\beta$ around 100, the transductance is about 40mA/V. Another way to look at it is an ideal transistor with $r_e = 1/g_m$ at its emitter. An ideal transistor is to have an infinite transconductance, which implies that $v_{be}$ approaches zero.  Let's use this model for the three basic amplifier configurations.  The gain of the common-emitter amplifier with emitter degeneration, $- R_C/(r_e + R_E)$. $r_e$ is about $25 \Omega$, which is generally much less than $R_E$, so the gain simplifies to $-R_C/R_E$, independent of the transistor parameters.  For the common-collection amplifier or the follower, the gain is a simple voltage divider, $R_E/(r_e + R_E) = 1/(1 + r_e/R_E) \approx 1$.  For the common-base amplifier, the gain is $r_e/R_C$.

Now we have raised the concept of an ideal transistor, let's be more clear.  The transconductance is infinite.  Because we have been using the small-signal model, which is a linearized model around the bias point, we have ignored the biasing.  An ideal transistor would require no biasing, no input current into the base  and the emitter current is bi-directional, flowing both in and out the emitter.   Such an ideal transistor has been approximated by an integrated circuit that is called an operational transconductance amplifier (OTA) or a diamond transistor.   One example is Burr-Brown (TI)'s OPA860.  It has a high impedance input as the base and a bi-directional emitter and requires no bias.  The transconductance is adjustable, but is around 100 mA/V or equivalently $r_e = 10 \Omega$.  So the transconductance gain is not quite infinite, but $r_e$ is small enough compared to $R_E$.


Wednesday, April 20, 2016

USB Charger

When charging my tablet using a bench supply, I notice that it only draws 0.45A, staying within the 0.5A limit.  But the tablet is capable of charging at higher current.  After researching on line, according to Battery Charging Specification, I found if the data lines were short to a resistance of 200 Ohms or less, it would be detected as a Charging Downstream Port, which supplies a minimum current of 1.5A.  I connected a 130-Ohm between D+ and D-; the charging current now reaches 1.1A.
The tablet charging circuitry seems also to check the voltage and dials down the current if the voltage drops.  When I set the supply voltage to 5V (the voltage at the tablet is less because of the drop on the wires), the current drops to 0.84A and 0.43A at 4.5V.  It reaches max 1.13A at 5.2V.

Monday, December 7, 2015

PS/2 Keyboard Emulator

One of my computers had a CMOS checksum error and waited for me to press a key to proceed.  I usually talked to this computer running Linux through the network or the serial console from my laptop.  In these days of smart phones, tablets and laptops, a regular keyboard has become scarce.  I did not have a regular keyboard around.  This computer is old enough to still have a PS/2 keyboard connector.  I figured that I should just build a PS/2 keyboard emulator.  This idea was to have on-screen keyboard that sent the keyboard scan code through a USB serial device to a microcontroller which in turn would generate the PS/2 keyboard signals.

The first thing was to get a 6-pin mini-DIN connector.  I found one on the USB mouse to min-DIN adapter.  The adapter is entirely passive; the USB mouse evidently switches the mode when it detects it is not connected to a USB host.  I pulled the adapter apart and had to remove the metal housing so I could solder wires to the pins.  I wired to the keyboard/mouse combo connector pinouts.
Pin # Signal Name
1 Mouse data
2 Keyboard data
3 Ground
4 Vcc
5 Mouse Clock
6 Keyboard Clock 
Pin 2 and 6 are normally NC for standard keyboard and mouse.


I chose to use the CY8CKIT-094-42xx PSOC prototyping kit, which only costed $3.99 and had a USB serial converter built-in.  And it is a 5V device, so it is compatible to the PS/2 interface.  The PS/2 keyboard/mouse interface consists of two open-drain signals, CLOCK and DATA.  When I measured the pull-up resistance, I was surprised to find they were 1K Ohms on the computer.  The communication is bidirectional.  The data is 11-bit: 1 start bit 0, 8 data bits lsb first, 1 odd parity bit and 1 stop bit 1.  First check if the clock and the data are idle (high).  The data bit is driven when the clock is high and the computer samples it when the clock goes low.  The clock period should be 60 to 100us and generated by the device.  I chose 80us period.  It seemed rather simple to implement.  I would ignore the PC to the keyboard communication for now.  The great thing about developing for the PSOC microcontroller is the PSoC Creator which takes care of the hardware peripherals graphically.  The low level code is generated automatically without having to find out the register definitions.  I quickly dragged out a UART component, a timer and a few I/O ports and configured them graphically.  And the PSOC architecture is the most flexible in the components allocations and pin assignments.  I did have to look at the API definitions or the code to find what functions to call but it was easier than writing the code myself.  In a short time, I had the UART communication, could toggle the I/O ports and had a 1KHz timer interrupt.  The timer counts would provide an 1us resolution clock and be used for the timing of the CLOCK.  The micrcontroller would be only responsible for clocking out the data received from the serial port.  The CY8CKIT comes with a boot loader; my application would have to configure as bootloadable.  The CY8CKIT enters the boot loader when it is powered up with the button pressed.  The Bootloader Host program downloads the code through the USB serial port.

Next I needed a way to get the keyboard scan code to the microcontroller.  I decided that I would use python with tkinter to get the keyboard event.  The event provides a keycode to every key event.  But the keycode is not the same as the keyboard scan code, so has to be translated.  And an extra code F0 has to sent for the key release event.  The majority of the keyboard scan code is just one byte; a few have an extra byte E0 prefix, which has to be sent before F0 for the key release.  I used PySerial for the serial I/O.

I verified the serial data were transmitted correctly by echoing them back.  I coded the keyboard clock and data I/O.  It was ready to go.  I plugged in and hit the key, but nothing happened.   So it was debugging time.  I checked the wiring again and found nothing wrong.  When probed the signals, I was surprised to find the signals were only at 2.5V.  So it was not just straight 1KOhms pull up.  I added 2KOhms pull-up resistors to both signals and they went up to 5V.  I also noticed that the computer kept pulling the CLK low for 180us every 100ms.  When I looked at the signals that the microcontroller generated, the timing was not correct.  I expected to see the CLK period to be 80us, but I saw 2ms.   So the timer was not doing what I expected.  I changed settings for the timer and tried other counters.  But I was not able to change timing.  I decided to forgo the timer component and use the ARM SysTick timer.  Still the timing did not change.  I was using the timer to interrupt at 1KHz and reading the counter value for the microsecond resolution.  The interrupt worked but the counter value did not seem change.  I decided to change the interrupt to 100KHz for 10us resolution and used it as the time base.  All the sudden, it worked: the computer responded to my keystrokes.  I thought I might have to to handle the PC to keyboard commands and apparently it could work without it for BIOS.  But Linux probably does keyboard detection and the keyboard would have to respond to commands.

Why the counter value was not read correctly was rather puzzling.  I would have to investigate later.

Wednesday, December 2, 2015

AT91SAM7 Code Development

I recently had to develop code for the AT91SAM7 microcontroller.  AT91SAM7 has an ARM7TDMI processor core, an architecture that has been superseded by Cortex-M3.

The first thing was how to program the on-chip flash memory.  The board was an old existing board which has the JTAG connection.  OpenOCD could be an option if I could make use of an JTAG dongle (I had a Stellaris eval board).  But AT91SAM7 had other ways of in-system flash programming.  Atmel supplied the SAM Boot Assistant (SAM-BA) for ISP through the COM or USB port; it was available for both Windows and Linux.  There was also an open source SAM-BA application, BOSSA.  I installed Atmel's SAM-BA and connected the device and installed the device driver (CDC).  The device showed as a COM port.  I started the SAM-BA application and specified the port and hit Connect, but nothing happened.  After a little search and reading the device manual, I realized that I needed to restore the SAM-BA boot, which is done pulling TST, PA0, PA1 and PA2 high for 10 seconds at the power up.  Unfortunately, none of these pins had been brought out.  So I had to solder a few hay wires to a LQFP-64 device.  Once the SAM-BA Boot was restored to the first two sectors of the on-chip flash, the SAM-BA application then worked and displayed the memory.  Later I realized that PA0-2 were pulled up internally, so only TST needed to be pulled up.

The second thing was to get the compiler toolchain.  GCC was the obvious choice.  ARM GCC could be downloaded from either launchpad.net/gcc-arm-embedded or Atmel.  The current version was 4.9.3.   I installed the version from launchpad.  I took the simplest example from at91sam7s-ek and modified slight to just blink one LED.  The compilation went smoothly and a binary file  was generated.  I used SAM-BA to write it to flash and verified it was written correctly.  After power cycling, nothing happened and the USB device was no longer recognized, which was understandable since the SAM Boot was overwritten.  There was no indication the code was being executed.  I suspected the compiler, perhaps it was not the right version to support the older device.  I tried the Atmel distributed GCC, which was also 4.9.3, but no luck.  I was also ready to go through my stash to find the Stellaris JTAG board.  I downloaded WinARM, the last version was 20060606, GCC 4.1.1.  I compiled with that, but still did not work.  The desperation started to set in.  Then I decided to try Keil MDK-Lite 4.50, that I had used before.  I tried the Blinky project with slight modification and downloaded with SAM-BA; it worked.  That was a relief; but MDK-Lite had the 32KB code/data limitation, so now I had to find out how to make GCC work.  I went back to try various versions of GCC, but same result.   I recalled that Atmel stated that their code was compiled by the yagarto.  I installed yagarto-20121222 (GCC 4.7.2) and it worked.  I would like to figure out what the problems with the other distributions, but now it was the time to move on.

The UART serial port was the next thing.  It just worked, no Herculean struggle here.  It worked to 230400 baud.  I needed 460800 baud, but the integer divisor caused too much error.  So I first tried 8x oversampling instead of the usual 16x, but no effect.  Then I tried the fractional divisor, also no effect.  It started getting puzzling.   Reading the manual again, I realized that I was using the debugging serial port instead of the regular USART and it appeared that both the 8x oversampling and the fractional disivor were not supported by DBGU.  I used it because it was had a connector and the USART0 and USART1 pins were not brought out.  I had to solder some haywires again.  I chose USART1.  It did not work initially until I realized that the DBGU library code was interfering with the printf that I tried to use.  After I left out the DBGU code, USART1 worked to 460KBaud.

I could finally get to interface the external device on the SPI bus.  After reading the data sheet, I set up the timing, polarity and other options.  I figured that I'd better check the timing on the scope before I connected the external device.  I saw the SCK and MOSI signals on the scope when I run the code,  but CS did not assert..  After chasing around a little, I realized it was mapped to the wrong pin by using Atmel's board definition.  I hooked up the external device and got zeros initially.  I tweaked the code for reading and it worked.

Now all pieces were in place to complete the application.  And I would optimize with using interrupts and DMA later.