Monday, September 7, 2009

Jaluino v1.1: enhanced Arduino-like board

Jaluino, with user guide, schematics, PCB, shields, etc... can now be reached on the following sites:

Main site: http://jaluino.org
Repository: http://jaluino.googlecode.com
Group: http://groups.google.com/group/jaluino




Last time, I built Jaluino v1.0, the very first version of Jaluino. Jaluino is an Arduino-like board (some even say a clone), based on a Microchip PIC 18F4550, and powered by jalv2 and jallib. This first version was very encouraging, but several points had to be improved. Quite a lot of has been discussed, now has come the next version: Jaluino v1.1


What's been improved ?

Several important points were discussed and improved:

  • i²c support. Jaluino v1.0 lacked i²c support, this new Jaluino v1.1 version now provides a new connector, and two jumpers to select whether the board should act as a master or a slave (more on this on a later tutorial I guess). This is an important point, as i²c is widely used. Jaluino now provides mostly important communication connectors: serial, USB, ICSP and i²c.
  • 3.3V power supply. Lots of components uses this voltage. Jaluino v1.1 provides 3.3V through the power supply connector. There are several options to choose the regulator: LM3940, LD1117V33, LM1117T, LM1086, LF33V, ... Not all have the same pinning. For now, we provide two options:
    • Input/Ground/Ouput pinning: use LM3940, LF33V. See Jaluino v1.1a.
    • Ground/Output/Input pinning: use LD1117V33, LM1117T, LM1086. See Jaluino v1.1b.
  • More space between connector shields. Last version revealed space between shield connectors weren't big enough, you couldn't put a mini-breadboard between them. This time, connectors have more space, slightly more than original Arduino's.
(also, because Richard, who designed the board, doesn't like vias, he removed one :))

Schematics and PCBs

  • Jaluino v1.1a: Input/Ground/Ouput pinning, using LM3940 or LF33V. See schematic and PCB.
  • Jaluino v1.1b: Ground/Output/Input pinning, using LD1117V33, LM1117T or LM1086. See schematic and PCB.
Note: by the time of writing, files are being renamed, in any case, go to Jaluino directory here, and select the appropriate files. Eventually, there will be one version remaining, to keep the whole thing simple...)


Results...

Here are couples of photos showing the results. The version built is Jaluino v1.1b, expecting to use LM1086 3.3V regulator. But that doesn't make a difference, since I'm still waiting to receive the part :). So note there's no 3.3V regulator on the board, yet it's working with 5V...

(this is not the best board I built, I had quite some problems mostly due to my etchant being too saturated, so expect the more beautiful board next time !)


Most components used to build Jaluino v1.1, except LM1086 and few caps



Jaluino v1.1 is born !


(ugly) Copper side


Connectors, from left to right: USB, serial, i²c and ICSP


Jaluino v1.1 and Wisp648, programming step


Wisp648 can directly be connected to Jaluino ISCP connector, making easy to program chip on-board


Another one :)


Jaluino v1.1, blinking a LED (while waiting for a development shield...)


Power connector supplies the breadboard with +5V


LED is connected to RA0, a wire is used to connect this pin to the breadboard


Breadboard view...


So... I need to "practice" this new Jaluino v1.1 board. Next steps are:
  • documentation, explaining, for instance, how to use jumpers, etc...
  • tutorials, showing how to use the board, with jalv2 & jallib (please provide some suggestions about covered topics)
  • shields building, an intermediate shield acting as a converter between Jaluino's and Arduino's original shields, and a development shield, to experiment with LCD, ADC, PWM, sounds, buttons, ... Any more suggestions ?


Sébastien Lelong & Richard Zengerink

Tuesday, August 25, 2009

Building Jaluino v1.0

Jaluino, with user guide, schematics, PCB, shields, etc... can now be reached on the following sites:

Main site: http://jaluino.org
Repository: http://jaluino.googlecode.com
Group: http://groups.google.com/group/jaluino




Big time.

Thanks to Richard's last contribution, we now have a nice schematics for the first version of Jaluino board, and very clean and easy to build PCB.

Jaluino is aimed to be a clone of Arduino, but PIC-based, and powered by jalv2 & jallib. The purpose is to share a common hardware configuration, on top of which one can add different features and capabilities, thanks to shields (daughter boards). Ala Arduino...

This post is to give an overview of how to build it, but with not so much details. If you want to build it, you'll have to be familiar with PCB creation.

That said, having this board within your hands is really nice ! It really helps thinking about what could be done with it, what could be improved, and is a nice base to start thinking about building shields.

So, here we go ! First of all, you can find all schematics, PCB and many more details in jallib SVN repository, under "/project/jaluino" directory.

Here's the schematic, but have a look at the PDF file for a more detailed one:




The PCB is surprisingly very simple (thanks Richard !), and thus very to easy to build:


Don't print and use this image ! Use the PDF file here, and print it at scale 1:1, that is, without any zoom-out or zoom-in correction. If any doubt, once printed, put the microcontroller support on the paper (or the like) and check all pins perfectly fit all holes.


PCB creation


As said, PCB creation is easy. I use the toner transfert method, using glossy photo laser paper (dedicated to laser printer, not ink-jet). If interested, have a look at this post, explaining how to do this, step by step, with a video.

Once the transfert is done, the PCB is ready to be etched.


PCB is etched, now drill the holes


PCB is ready for soldering


A brief comparison between original Arduino and Jaluino. Jaluino is obsviously bigger, but embed a much bigger chip, and most importantly is made homebrew!


Soldering components


I try to start soldering components from the smaller & lower ones, to the biggest and taller ones. We'll follow this component diagram, provided in PCB PDF file (page 2).



First solder resistors and diodes


Then solder small capacitors (unpolarized)


Place microcontroller support, quartz, pushbutton and bridge rectifier (I used a DF02M instead of DF04M)


Add wires (green), power jack, big capacitors (polarized)


Solder shield connectors, serial and ICP connects. Add 7805 voltage regulator. Don't forget jumper for power selection and RTS reset feature.


Place USB connector and fuse support


Place your favorite 18F4550 onto the support, and don't forget the fuse ! You're done, kudos !


The reverse side...


Now here's a brief map showing the different features coming with this board.


Note: one important missing feature is I²C support, which surely will come in the next version.

I already tried quite few things with this board: In Circuit Programming using XWisp648, blinking a LED (of course), UART using serial connector, with tinybootloader and testing Reset-via-RTS feature (great), and USB-to-serial ! In the next post, I'll try to describe these experiments one by one.

Another important thing, if not the crucial one, is how big should a shield board must be. Current shield connectors configuration gives a surface closed to Arduino's (only considering the surface between connectors, not the whole board). It it enough ? Should a shield board be bigger ? Now imagine this board is doubled-sided, built through a professional process (and thus available as a commercial product). How much smaller would it be ? Will current connectors configuration be valid ? This is a very important thing, because from this is derived all the shield compatibility. If one builds a shield for this Jaluino v1.0, and shield connectors gets changed, this shield will have to be adapted and rebuilt...

And about shields, now the time has come to design a development shield, providing (more or less) basic features. This shield could be used to learn jalv2 & jallib. I was thinking about having:
  • one LED connected to a standard digital output
  • one LED connected to a PWM channel
  • maybe a buzzer connected to the other PWM channel to make sounds
  • one or two push-buttons (input)
  • pot to have fun with ADC
  • maybe an EEPROM to play with I²C
  • a small LCD screen (8x2)
But maybe that's a little bit too much for single shield...


Please advise !



Sébastien Lelong & Richard Zengerink.

Monday, August 10, 2009

PIC 18f14k50 based USB WISP programmer

Introduction
Today the description of a USB WISP programmer. A while back I had some trouble with my ICD2 clone, therefore I decided to make a WISP programmer. Since I've written the USB libraries for JAL(lib) and because I just had ordered a tube of 10 PIC18f14k50, I decided to redesign the original WISP so it uses the PIC18f instead of the the PIC16f648 (which I did not have in my junk box), and use the USB-Serial library so I can still use the various host applications but I can plug it in directly to the USB port. The additional benifits are:

  • Very cheap (just a PIC18f14k50 + resistor and capacitors, total costs ~5 Euro)
  • Have a fast serial connection
  • Can use exsisting WISP host applications (like Rob's XWISP2)
  • Can be directly powered from the USB port (no external power supply needed)
  • When adding a 3.3V regulator, board can also be used to program LF devices (if Vpp = 9v)
  • Added an additional connector for future use, like JTAG & I2C and RS-232 TTL

The schematic is very straigtforward, the blue boxed areas contain the optional section of the USB WISP programmer





I've made a few modifications to the original design, first the Target power is controlled by a PNP transistor, which acts as switch (so target power can be turned on/off), because USB 5v does not like to be short circuited for a short period, as is done in the WISP 648. In addition I've added a PNP transistor to control the Target VPP line, this is for future use.

I also made a small PCB, however, Wouter van Ooijen (the designer of the Wisp 648 programmer, see www.voti.nl) asked me not to publish the PCB layout. So don't ask me for it, if you want to build this project you have to design you own PCB, if you can't, buy a Wisp 648 kit from Wouter. The assembled PCB is depicted below:




The Source code
I just made minor modifications to the original source code (see www.voti.nl). The changes are:
  • Include usb_serial library
  • Change UART functions, so they send and receive chars from the USB library
  • Tune the timing delays (becuase effective speed is raised from 20->48 MHz)
  • Had to adapt the HEX<->ASCII function, because the original code still uses some inline assembly which did not work properly for the PIC18f
  • Call USB-Flush in the main routing

There still some work left, i.e. the port trough function is not working yet, the advantage of this design is the the UART can be used (even the polarity inversion of the UART signals can be programmed with the PIC18f14k50), so pass trough can work with speeds up to 115200 baud.
The JAL source code is uploaded in the JALLIB google code file download section, http://groups.google.com/group/jallib/files

Results
The USB Wisp is operation for a couple of weeks, so far it worked flawlessly. With a small change in the XWISP2 configuration file, I was able to program the PIC14k50 as well. But of course, there is a big discalaimer, I only have a couple of PICs, so the solution is not tested extensivly, thus if you want a good and robust PIC programmer I advice you to buy or build the original WISP 648.

Conclusion
This small exerise shows the strenght(s) of JalV2 and Jallib, it shows how easy it is to port a large application from a PIC16f devices to a PIC18f device and that only a little effort is required to move from a RS-232 (UART) communcation interface to a USB (serial) based communication interface.






Sunday, August 2, 2009

Jaluino Medium 18F2550-based: first draft



Jaluino, with user guide, schematics, PCB, shields, etc... can now be reached on the following sites:

Main site: http://jaluino.org
Repository: http://jaluino.googlecode.com
Group: http://groups.google.com/group/jaluino



Careful, schematic and PCB in this post aren't tested yet. Except if you're a daring risk-taker, you shouldn't build this board as it would only be a waste of time, and probably of money too... You've been warned.

I'm still on this Jaluino board.

While trying to get a working schematic, I've also spend a lot of time drawing the PCB. I focused on a 18F2550 version, instead of big fat 18F4550, so PCB layout is simpler and I can focus more important issues for now. So this is now about Jaluino Medium (18F2550), not Jaluino Maxi (18F4550). BTW any ideas about "marketing" names to replace Mini, Medium and Maxi ?...

Last schematic contained lots of errors, mostly on USB and dual power. Luckily Guru Albert helped and provided me four options. I kept the simpler one (I can understand), which consists in having a jumper to select whether power should come from:
  • external supply (jack)
  • USB self-powered connector
(I would even add a new source of power: from serial, and more specifically from a USB-to-serial converter)

The current schematic seems to look better (yet still untested, this will be for other posts).



So here how the PCB looks like now...



This PCB is expected to be 8.1cm x 7.1cm (3.2in x 2.81in). Compared to an original Arduino (6.9cm x 5.4cm), it's a little bit bigger. But it's also a single side board, and there are lots of other connectors (I²C and serial) not present in Arduino.

There are several connectors. Some will be the shield connectors (JP1, JP2, JP4, JP5 and POWER), female headers as in Arduino, others (ICSP/JP6, I²C, serial) will be male headers.

The shield is expected to be something like 4.8cm x 5.8cm (1.9in x 2.3in). Original Arduino's shield is 4.9cm x 4.3cm. It's a little bit bigger, and I may adjust connectors (move JP1 & JP2 lower) to have a "more squared shape".


Next posts will be about testing and prototyping this board, using a breadboard. This will also be a good exercise to have basic (and maybe advanced) experiments using jalv2 and jallib.


Sébastien Lelong

Thursday, July 30, 2009

PIC 18f14k50 USB Interface Board: Part 3

Introduction
As promised, a blog about USB-HID devices. This blog will demonstrate how to create a USB-HID keyboard device running on the PIC 18f14k50 USB Interface Board. Making a HID device is a bit more complicated that getting the USB serial communication, becuase there are a wide range of HID devices, it is very hard to make a generic library. However, HID devices are nice, because you don't need a special drivers on the Host PC. There are many HID devices that can directly interact with the host operating systems, like keyboards, mice, joysticks etc, it is even possible to make a generic HID device, which can be controlled from a host application (but I will save that for another blog). Today were making a keyboard device, once you press the boot button it will generate a character. It is an educational example and does not have any practical use, but you can use it as a starting point to make for example a IR remote receiver that generate keystrokes.

The Code
I've splitted up the code description in a couple of section, and it will explain it step by step. We start of with the regular stuff, not very existing and I won't explain it in more detail

-- chip setup
include 18f14k50
pragma target clock 48_000_000

include delay

Before creating the USB descriptors, we need to include the USB constant definitions by

include usb_defs

Ok, that was the easy stuff. USB works with enpoints, and we have to indicate which endpoints will be enabled, where there are located and specify their size, and endpoint can be considered more or less a communcation channel between the Host PC and the PIC device. An endpoint can consist of an input endpoint (seen from the Host computer, so output for the PIC device) and an output endpoints (again, seen from the Host computer, so input for the PIC device). An USB device should always have input & output enpoint zero, which is used for e.g. exchanging device configuration settings. In this example we will have and additional enpoint, which is used for the exchange of HID reports (i.e. the keyboard information). Furthermore, the an enpoint consist of two parts, the Endpoint Descriptor and the Enpoint Buffers, the setup of the Endpoint Descriptors are taken care of by the USB library, however, the Enpoint buffers have to be setup by the user of the library. The following code shows how to enable the endpoints and Endpoints Buffers:

const bit USB_EP0 = 1
const byte USB_EP0_OUT_SIZE = 8
const word USB_EP0_OUT_ADDR = ( USB_BASE_ADDRESS + 0x0010 )
const byte USB_EP0_IN_SIZE = 8
const word USB_EP0_IN_ADDR = ( USB_EP0_OUT_ADDR + USB_EP0_OUT_SIZE )

const bit USB_EP1 = 1
const byte USB_EP1_OUT_SIZE = 8
const word USB_EP1_OUT_ADDR = ( USB_EP0_IN_ADDR + USB_EP0_IN_SIZE )
const byte USB_EP1_IN_SIZE = 8
const word USB_EP1_IN_ADDR = ( USB_EP1_OUT_ADDR + USB_EP1_OUT_SIZE )

const bit USB_EP2 = 0
const bit USB_EP3 = 0

var volatile byte usb_ep1in_buf[ 8 ] at USB_EP1_IN_ADDR


const bit USB_EPx = 1 indicates that the endpoint is enabled, if the endpoint is enabled the address of the Endpoint buffer must be specified, in addition, it should map within a special memory region. The USB hardware "talks" with the microcontroller via a dual access memory, this memory location is dependent on the PIC type, however its base addres USB_BASE_ADDRESS constant is defined usb_defs, so we can use this constant to setup the memory regions for the usb buffers independent of the PIC type. You might wonder why the enpoint buffers are not mapped at the beginning of the memory regions (USB_BASE_ADDRESS), this is because the USB library needs to setup the Endpoint descriptors, they shall be located starting at the beginning of the dual access memory. To calculate the start of the memory region that can be used for the Endpoint buffers, take the highest enpoint number, add one, and multiply it by 8. So in this example we have endpoint 0 and 1, so 2 x 8 = 16 (0x10 hex).

Well, that just beginning, next "record" we have to specify is the USB_DEVICE_DESCRIPTOR, it consists of 18 bytes and describes the device properties. Take a look at the usb specifiction (see http://www.usb.org/) for the details.



const byte USB_DEVICE_DESCRIPTOR[USB_DEVICE_DESCRIPTOR_SIZE] = {
USB_DEVICE_DESCRIPTOR_SIZE, -- 18 bytes long
USB_DT_DEVICE, -- DEVICE 01h
0x00,
0x02, -- usb version 2.00
0x00, -- class
0x00, -- subclass
0x00, -- protocol
USB_EP0_OUT_SIZE, -- max packet size for end point 0
0xd8,
0x04, -- Microchip's vendor
0x55,
0x00, -- Microchip keyboard demo
0x01,
0x00, -- version 1.0 of the product
0x01, -- string 1 for manufacturer
0x02, -- string 2 for product
0x00, -- string 3 for serial number
0x01 -- number of configurations
}

const byte USB_STRING0[] =
{
0x04, -- bLength
USB_DT_STRING, -- bDescriptorType
0x09, -- wLANGID[0] (low byte)
0x04 -- wLANGID[0] (high byte)
}

const byte USB_STRING1[0x36] =
{
0x36, -- bLength
USB_DT_STRING, -- bDescriptorType
"M", 0x00,
"i", 0x00,
"c", 0x00,
"r", 0x00,
"o", 0x00,
"c", 0x00,
"h", 0x00,
"i", 0x00,
"p", 0x00,
" ", 0x00,
"T", 0x00,
"e", 0x00,
"c", 0x00,
"h", 0x00,
"n", 0x00,
"o", 0x00,
"l", 0x00,
"o", 0x00,
"g", 0x00,
"y", 0x00,
",", 0x00,
" ", 0x00,
"I", 0x00,
"n", 0x00,
"c", 0x00,
".", 0x00
}

const byte USB_STRING2[42] =
{
42, -- bLength
USB_DT_STRING, -- bDescriptorType
"J", 0x00,
"A", 0x00,
"L", 0x00,
" ", 0x00,
"H", 0x00,
"I", 0x00,
"D", 0x00,
" ", 0x00,
"K", 0x00,
"e", 0x00,
"b", 0x00,
"o", 0x00,
"a", 0x00,
"r", 0x00,
" ", 0x00,
" ", 0x00,
"D", 0x00,
"e", 0x00,
"m", 0x00,
"o", 0x00
}


I'll not go into all the details of the USB descriptors, however, we need to specify USB_STRING0..3 because we filled in the USB string identifiers in the USB_DEVICE_DESCRIPTOR record. These string have to conform a predfined format, i.e. the first byte should specify the total record length, and the second byte indicates that it is a string USB_DT_STRING, furthermore, it has to be a unicode string, therefore after each character a 0x00 value needs to be defined. USB_String0 is a special string, since it specifies the language code, which is set to US English. Furthermore we specified there is one configuration record (last byte in the USB_DEVICE_DESCRIPTOR record.

The USB_CONFIGURATION_DESCRIPTOR record specifies the properties of the USB device, again the details are available the USB specification at http://www.usb.org/. High level description is that were setting up one configuration record for a HID device, which specifies the HID interface by pointing to the HID descriptor and it will have one endpoint (is number of enpoint exluding enpoint 0 which should always be present).










const byte USB_HID_REPORT1[]=
{
0x05, 0x01, -- USAGE_PAGE (Generic Desktop)
0x09, 0x06, -- USAGE (Keyboard)
0xa1, 0x01, -- COLLECTION (Application)
0x05, 0x07, -- USAGE_PAGE (Keyboard)
0x19, 0xe0, -- USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, -- USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, -- LOGICAL_MINIMUM (0)
0x25, 0x01, -- LOGICAL_MAXIMUM (1)
0x75, 0x01, -- REPORT_SIZE (1)
0x95, 0x08, -- REPORT_COUNT (8)
0x81, 0x02, -- INPUT (Data,Var,Abs)
0x95, 0x01, -- REPORT_COUNT (1)
0x75, 0x08, -- REPORT_SIZE (8)
0x81, 0x03, -- INPUT (Cnst,Var,Abs)
0x95, 0x05, -- REPORT_COUNT (5)
0x75, 0x01, -- REPORT_SIZE (1)
0x05, 0x08, -- USAGE_PAGE (LEDs)
0x19, 0x01, -- USAGE_MINIMUM (Num Lock)
0x29, 0x05, -- USAGE_MAXIMUM (Kana)
0x91, 0x02, -- OUTPUT (Data,Var,Abs)
0x95, 0x01, -- REPORT_COUNT (1)
0x75, 0x03, -- REPORT_SIZE (3)
0x91, 0x03, -- OUTPUT (Cnst,Var,Abs)
0x95, 0x06, -- REPORT_COUNT (6)
0x75, 0x08, -- REPORT_SIZE (8)
0x15, 0x00, -- LOGICAL_MINIMUM (0)
0x25, 0x65, -- LOGICAL_MAXIMUM (101)
0x05, 0x07, -- USAGE_PAGE (Keyboard)
0x19, 0x00, -- USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, -- USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, -- INPUT (Data,Ary,Abs)
0xc0
}
const USB_CONFIGURATION_DESCRIPTOR_SIZE = 0x09 + 0x09 + 0x09 + 0x07

const byte USB_CONFIGURATION_DESCRIPTOR[ USB_CONFIGURATION_DESCRIPTOR_SIZE ]=
{
-- configuration descriptor - - - - - - - - - -
0x09, -- length,
USB_DT_CONFIGURATION, -- descriptor_type

USB_CONFIGURATION_DESCRIPTOR_SIZE,
0x00, -- total_length;

0x01, -- num_interfaces,
0x01, -- configuration_value,
0x00, -- configuration_string_id,
0b10000000, -- attributes (bus powered, no remote wake up)
100, -- max_power; (200ma)

-- interface descriptor - - - - - - - - - - - -
0x09, -- length,
USB_DT_INTERFACE, -- descriptor_type,
0x00, -- interface_number, (starts at zero)
0x00, -- alternate_setting, (no alternatives)
0x01, -- num_endpoints,
USB_HID_INTF, -- interface_class, (HID)
USB_BOOT_INTF_SUBCLASS, -- interface_subclass, (boot)
USB_HID_PROTOCOL_KEYBOARD, -- interface_protocol, (keyboard)
0x00, -- interface_string_id;

-- hid descriptor - - - - - - - - - - - - - - -
0x09, -- length,
USB_DT_HID, -- descriptor_type;
0x11,
0x01, -- hid_spec in BCD (1.11)
0x00, -- country_code, (0=not country specific)
0x01, -- num_class_descriptors, (1)
USB_DT_HID_REPORT, -- class_descriptor_type; (0x22 = report)
(count( USB_HID_REPORT1 ) & 0xFF ),
(count( USB_HID_REPORT1 ) >> 8 ),

0x07, -- length,
USB_DT_ENDPOINT, -- descriptor_type,
0b10000001, -- endpoint_address, (Endpoint 1, IN)
USB_EPT_INT, -- attributes; (Interrupt)
USB_EP1_IN_SIZE,
0x00, -- max_packet_size
0x01 -- interval (1ms)
}










The HID Report specifies which information can be exchanged between the Host PC and the
USB device. There are seperate specification available which describes the details of a HID report (again, see http://www.usb.org/).
Making a HID report can be quite cumbersome, and there are even tools available to make HID report. Anyhow, for now assume that the specified HID report can exchange keyboard related information between the Host PC and the USB device


OK that was the toughest part, now some real JAL code.
Tekengrootte


-- include remaining USB libraries
include usb_drv_core
include usb_drv

-- HID report layout (see USB specification for more details)
-- 0 Modifier byte
-- bit
-- 0 LEFT CTRL
-- 1 LEFT SHIFT
-- 2 LEFT ALT
-- 3 LEFT GUI
-- 4 RIGHT CTRL
-- 5 RIGHT SHIFT
-- 6 RIGHT ALT
-- 7 RIGHT GUI
-- 1 reserved
-- 2 keycode array (0)
-- 3 keycode array (1)
-- 4 keycode array (2)
-- 5 keycode array (3)
-- 6 keycode array (4)
-- 7 keycode array (5)
var byte hid_report_in[8] = { 0,0,0,0,0,0,0,0 }

var byte key_value = 4
var bit sw3 is pin_c2
var bit latched_sw3 = sw3


-- disable analog unit, all ports set to digital
enable_digital_io()

-- setup the USB device
usb_setup()

-- enable USB device
usb_enable_module()

-- set input where switch is attached to input
pin_c2_direction = input

-- main loop
forever loop
-- poll the usb ISR function on a regular base, in order to
-- serve the USB requests
usb_handle_isr()

-- check if USB device has been configured by the HOST
if usb_is_configured() then

-- prepare the HID buffer
if ( sw3 != latched_sw3 ) then

latched_sw3 = sw3

if ( sw3 == low )then
hid_report_in[2] = key_value
key_value = key_value + 1
if ( key_value == 40 ) then
key_value = 4
end if
else
hid_report_in[2] = 0
end if

-- Send the 8 byte packet over USB to the host.
usb_send_data(USB_HID_ENDPOINT, hid_report_in, count( hid_report_in ) , low )

-- debounce
delay_1ms(50)
end if
end if
end loop





First the remaing USB libraries have to be included, since the library need to have access
to the device and configuration records it has to be included after the definition of these recods. Next an array of 8 bytes is defined for the HID report were sending out from the device towards the HOST. The first byte is a modifier key, while the remaing bytes will contain the key character(s). More details can be found in the USB HID keyboard specification (see also www.usb.org). Keep in mind though that the characters you send are not ASCII character, for example the "a" character has a byte value of 4.
Next the switch is initialized, and all inputs are set to digital io. Then the USB will be initialized by calling the usb_setup() and usb_enable_module() procedures. Then the main loop, first it has to call the usb_handle_isr() procedure on a regular base, in order to service the USB hardware. Next it will check if the USB interface is already configure by the host, via the usb_is_configured() function. If the USB device is recognized by the Host OS, it will check for state changes of the program switch of the PIC 18f14k50 USB Interface Board. If the value has changed and the button is pressed, it will send a HID report (i.e. it will send a character to the Host PC). The value of the character starts at 4 (the "a" character) and will increase till the "z" character, when reached it will start over again with the "a" character. It will fill in the character in the HID report and then it sends the HID report via USB towards the Host PC by calling the usb_send_data() procedure. Which will send the data via endpoint 1.

Executing the demo
Compile the code (available at the Jallib SVN repository projects\pic14k50_usb_io\examples\blog_part3.jal) using the -loader18 flag. Reboot the PIC 18f14k50 USB Interface Board, while holding down the program and reset switch together and release the reset switch while keep holding down the program switch. After a couple of seconds you can release the program switch. Start the PDFSUSB.exe application to download the hex file. Once downloaded, press the reset button once again. After a couple of seconds the Host PC should recgonize the a new "Keyboard". Wait a couple of seconds, start an editor, for example notepad on the Host PC, if you now press & release the program button on the board, it should type a character within the editor, next time you press & release the program button it will show you the next character of the alphabet.

OK, a long story this time, I know not everything has been explained in detail yet about the Jallib USB libraries, but maybe in a future blog I can tell more about the inner working of the Jallib USB libraries. The next episode will likely building a Generic HID device.

Wednesday, July 29, 2009

PIC 18f14k50 USB Interface Board: Part 4

Introduction
This is a small follow up regarding the Jallib USB Serial library. Today's blog I'm showing how to make a more serious application with the PIC 18f14k50 USB Interface Board, namely a USB-RS-232 converter. I will go trough the source and explain the JAL code step by step.

Source code
The beginning is the same as Part 2, namely defining the target clockand including the device file.

1:  -- chip setup
2: include 18f14k50
3:
4: -- even though the external crystal is 20 MHz, the configuration is such that
5: -- the CPU clock is derived from the 96 Mhz PLL clock (div2), therefore set
6: -- target frequency to 48 MHz
7: pragma target clock 48_000_000
8:

Serial interrupt handling
Then we need to declare a forward procedure in order to receive the line setting change events, which will be discussed a bit later on. In addition the usb_serial library has to be included as well as the print library so for now we have to add the following code (line 1-7)

1:  -- forward procedure declaration
2: procedure usb_cdc_line_coding_changed_callback()
3:
4: -- include standard libraries
5: include delay
6: include usb_serial
7: include print
8:
9: procedure _serial_receive_interrupt_handler() is
10: pragma interrupt
11:
12:
13: if (PIR1_RCIF == TRUE) then -- UART receive interrupt
14:
15: if ((RCSTA_OERR == TRUE) | (RCSTA_FERR == TRUE)) then -- frame/overr error
16: var byte x = RCREG -- flush hardware buffer
17: while RCSTA_OERR == TRUE loop -- overrun state
18: RCSTA_CREN = FALSE -- disable UART
19: RCSTA_CREN = TRUE -- re-enable UART
20: x = RCREG -- \ flush hardware buffers
21: x = RCREG -- /
22: end loop -- until no more overrun
23:
24: else -- data without errors
25: var byte ch = RCREG
26: -- usb_serial_data = RCREG
27: usb_cdc_putc( ch )
28: end if
29: end if
30: end procedure
31:
32:
From line 9-30, the interrupt routine to handle the receiving of the UART characters. As discussed in the previous blog, the USB library is not interrupt driven, therefore the USB service procedure must be called on a regular base to service the USB hardware. Servicing can be time consuming therefore I've decided to make the RS-232 serial interface (UART) interrupt driven, so were sure that were not lossing incoming data on the UART. The incoming data will be placed directly in the USB transmit ring buffer by calling the usb_cdc_putc( ch ) procedure. The USB-Serial serving procedure checks if there is data present in the ring buffer.


Handling baudrate settings
Next code section is the handling of the baudrate settings. The USB serial library has a callback function usb_cdc_line_coding_changed_callback() , when defined, it will be called each time the USB Host will request the line coding setting (which contains the baudrate, parity, stopbits etc). The procedure entry is on line 45 in the code below, the procedure checks/limits the baudrate settings and will change the baudrate (line 2-23) and by re-initializing the UART hardware (line 27-42).

1:  -- procedure to change the baudrate settings of the UART
2: procedure change_baudrate( dword in baud_rate) is
3:
4: -- compiler issue with -const-detect
5: if true then
6: var dword fosc_div4 = dword( target_clock ) / 4
7: else
8: var dword fosc_div4
9: fosc_div4 = dword( target_clock )
10: fosc_div4 = fosc_div4 / 4
11: end if
12:
13: var dword div_factor = fosc_div4 / baud_rate - 1
14: var word div_wfactor = word( div_factor )
15: var byte div_btfactor[2] at div_wfactor
16:
17: TXSTA_BRGH = true
18: BAUDCON_BRG16 = true
19:
20: SPBRGH = div_btfactor[1]
21: SPBRG = div_btfactor[0]
22:
23: end procedure
24:
25:
26: -- Initializes the serial port, calculates baudrate registers.
27: procedure serial_hw_init() is
28: RCSTA = 0b0000_0000 -- reset
29: RCSTA_SPEN = enabled -- serial port enable
30: RCSTA_CREN = enabled -- continuous receive enable
31:
32: TXSTA = 0b0000_0100 -- reset (16 bit, asyn)
33: TXSTA_TXEN = enabled -- UART transmit enabled
34: -- TXSTA_SYNC = true
35: TXSTA_BRGH = true
36: BAUDCON_BRG16 = true
37:
38: PIE1_RCIE = enabled -- UART receive int. enable
39: -- (PIE1_TXIE dynamically)
40: INTCON_PEIE = enabled -- periferal
41: INTCON_GIE = enabled -- general
42: end procedure
43:
44: -- callback procedure, is called if the USB Host changes the line settings
45: procedure usb_cdc_line_coding_changed_callback() is
46:
47: if ( cdc_line_coding_dte_rate > 115200 ) then
48: cdc_line_coding_dte_rate = 115200
49: end if
50: change_baudrate( cdc_line_coding_dte_rate )
51: serial_hw_init()
52: end procedure
53:

The main loop
Finally we need to initalize the hardware and define the main loop. First all IO ports are set to digital (line 3) and need to initialize the USB serial library (line 6). Next a character is defined which holds the receiving character.

1:  
2: -- disable analog
3: enable_digital_io()
4:
5: -- setup the USB serial library
6: usb_serial_init()
7:
8: var byte ch
9:
10: -- main loop
11: forever loop
12: -- poll the usb ISR function on a regular base, in order to
13: -- serve the USB requests
14: usb_serial_flush()
15:
16: -- check if USB device has been configured by the HOST
17: if ( usb_cdc_line_status() != 0x00 ) then
18:
19: -- check for input character
20: while usb_serial_read( ch ) loop
21: -- echo input character
22: TXREG = ch
23: while ! PIR1_TXIF loop end loop
24: end loop
25:
26: end if
27: end loop
Finally the main loop, as said before, the usb_serial_flush() procedure has to be called on a regular base. This function takes care of transmitting the character to the USB Host that have been received by the UART, and it also takes care of putting the character that are sent from USB Host into a receive buffer.

A rough calculation on the maximum interval time between two USB Serial flush:
By default the USB-Serial library will create a ring buffer of 32 bytes, which is for this application more than sufficient (max UART speed is ~10.000 characters/second, so USB Serial flush must be called at least every 10.000 / 32 = 3.2 ms. So there is enough time left to perform additional taks

On line 17 it checks if the USB Host is connected, if so, it will put all character that were sent by the USB Host in the UART Transmit register (line 19-24).

That's about it, this blog showed how to create a simple USB-RS-232 convert, using the JALLB USB serial librray and the PIC 18f14k50 USB Interface Board. Possible extentions are to include RS-232 handshaking and to take other RS-232 line setting into account.



Sunday, July 26, 2009

PIC 18f14k50 USB Interface Board: Part 2

This is the second episode about the PIC 18f14k50 USB Interface Board. This time I want to take about the usage of the JALLIB USB-Serial library and how it can be used in conjuction with the interface board.

Introduction
USB is a fairly complex interface, besides the basic interface, there are defined a couple of higher level "protocols" which are target for specific "device" functions. Commonly used interfaces are the Human Interface Device(s) types, to support devices like keyboards, joysticks and other devices, another commenly used interace is the Communication Device Class (CDC), which is developped for a broad range of (serial) communication devices, like modems, RS-232 converters etc. HID devices will be discussed in another blog, today we're looking a bit closer on the CDC interface and how to talk to the PIC device with a terminal application via the USB interface.

In order to reduce the USB learning curve, we've devevlopped a special library (USB-Serial) to easily create firmware that behaves like a RS-232 port on the Host side (virtual COM port). The big advantage is that you can still use the good old terminal application(s) to talk with your PIC device via the USB interface (both on Windows and Linux and probably other Host operating systems)

As an example we use the 18f14k50_board_test.jal, we start of simple and show step by step how to use the usb_serial library.

USB Serial example code
We start of with a basic application, the code is listed below:

include 18f14k50                    -- target PICmicro

--
-- This program assumes a 12 MHz resonator or crystal
-- is connected to pins OSC1 and OSC2.
-- Configuration bits may cause a different frequency!
pragma target clock 48_000_000 -- oscillator frequency

-- include libraries
include usb_serial
include print

-- initialize the USB serial library
usb_serial_init()


-- main loop
forever loop

var byte ch

-- Service USB, call on a regular base to keep communcaiton going
usb_serial_flush()

-- check for input character
if usb_serial_read( ch ) then
-- nothing spectecular, echo ch + 1
if ch == "?" then
const byte str1[] = "Hi there \r\n"
print_string( usb_serial_data, str1 )
else
usb_serial_data = ch
end if
end if
end loop
--




The code is pretty straight forward, first the device file is included and the target clock speed is set. Then the usb_serial and print JALLIB libraries are included. The next step is to initalize the USB serial library by calling the usb_serial_init() procedure. Within the main loop, the usb_serial_flush() is called, so the USB serial Interface Enginge (SIE) is serviced on a regular base (the USB libraries are not interrupt driven, therefore this procedure must be called regulary.
The call towards usb_serial_read will check if there are character recieved from the USB Host, if a character has been received it will be echoed, unless the "?" character is send, then it will use print the "Hi there" string. Notice that the usb_serial library can be used in conjuction with the print and serial libraries, if the usb_serial_data argument is passed along (as the first argument) with the print related procedure calls.

Get it running
The code is compiled with jalv2 using the -loader18 -no-fuse. After compilation one can reset the PIC 18f14k50 USB Interface Board and holding down the program button. Now the PDFSUSB application can be used to download the HEX file file, after download reset the board. If everything went OK, it should recognize the USB-CDC device and ask for drivers (Windows only), select the driver (which can be downloaded from
http://groups.google.com/group/jallib/files, file usb_cdc_drivers.zip), by selecting the win2k_xp_vista32_64 directory. After installtion of the drivers, the host operating system will create a new (virtual) serial port.
After the (virtual) serial port is created, open the serial port with your favorite terminal appliction. The actual serial port settings does not really matter, once the serial port is opened, you can any send character, the character will be echoed unless the "?" mark character is sent, the it will respond by the "Hi There" sentence.

Wrap Up
So far about the usb_serial library, it shows how easy it is to create an PIC firmware application using which can comminicate with a Host PC via the USB interface. Upcoming blog will address more advanced usage of the USB interface by creating a HID device,.