Showing posts with label PIC JALLIB JAL USB SERIAL RS-232 INTERFACE. Show all posts
Showing posts with label PIC JALLIB JAL USB SERIAL RS-232 INTERFACE. Show all posts

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,.