Showing posts with label picbasic. Show all posts
Showing posts with label picbasic. Show all posts
0

Joysticks rule

Posted by Chris on Friday, July 17, 2009 in , , ,
...especially when they're attached to a Nokia 6610 breakout board. This little fella provides 4-axis input (plus a "fire" button) using a single input pin. It's quite clever really, but simply uses a load of resistors, to change the input voltage depending upon which direction has been pressed.
Put the input from the miniature joystick on the Arduino Nokia board onto pin A0 of a PIC microcontroller and read the input values.



The Oshonsoft PIC18 the command for reading an analogue input is:
Adcin 0, joystickvalue


This takes the input from analogue channel zero (in our case, PORTA.0) and writes it to the variable "joystickvalue".
During testing, with an open connection (joystick centred, no movements made) the joystick value wavered between 169-170. At first this seems odd, but looking at the schematic datasheet we can see that the reference voltage from the joystick is between 0v and 3.3v.

Taking the average value of 170, and given that we're storing this data into a byte variable, we can see that (170/255)*5v = 3.3v.
So it all makes sense again - with no joystick movement, we expect to receive 3.3v from the joystick onto our analogue pin. And the value 170 does indeed represent a 3.3v signal when our PIC is powered by a 5v supply.

A simple routine was put together to write the value of the joystick input onto the Nokia screen. The following was determined:



This translates quite nicely into a simple subroutine for any project that uses this board. The routine waits for the joystick to be moved, stores the direction into a variable then waits for the joystick to be released again.
The routine can be called from anywhere in the program, though it might work quite nicely if called from a timer interrupt, since the user input is stored until the microcontroller is free to process it.


Dim joystickvalue as Byte
Dim jstick(5) as Byte
Dim buttonpressed As Byte

jstick(1) = 126 'right
jstick(2) = 88 'up
jstick(3) = 60 'down
jstick(4) = 26 'fire
jstick(5) = 4 'left



getbuttonpress:
  'single input ADC for joystick press
  Adcin 0, joystickvalue

  'compare current joystick value to stored values
  'at rest returns 172-168, first value is 120-122 for "right"

  If joystickvalue < 140 Then
    For i = 0 To 4
      If joystickvalue < jstick(i) Then
        buttonpressed = i
      Endif
    Next i

    'debounce the button press
    WaitMs 1
    Adcin 0, joystickvalue
    If joystickvalue < 140 Then
      'wait for the button to be released
      While joystickvalue < 140
        'keep sampling the joystick to see whether it's been released
        Adcin 0, joystickvalue
      Wend
    Else
      buttonpressed = 0
    Endif

  Else
    buttonpressed = 0
  Endif

Return



0

It's one small step for (a) man.....

Posted by Chris on Friday, July 10, 2009 in , , ,
....one giant leap for Nokia 6610 colour LCD display boards!
Armed with little more than a Phillips PCF8833 datasheet and some Arduino code libraries, excitement reigned around Nerd Towers today, as this little colour LCD Arduino shield flickered into life.

The screen uses SPI to send byte commands to the screen.
After initialising the screen, data is written using the following format:

First a "window" is defined, by providing start-x, end-x, start-y and end-y co-ordinates (0-131). Then, a stream of data is sent, with (in 8-bit mode) each byte representing a pixel colour.
You can define how the screen behaves after each pixel-byte is received. I set my display up to draw pixels from top-to-bottom. When the current pixel reaches the bottom of the defined window size, it increases the column counter and returns to the top of the window



Unlike most serially-driven devices, the Nokia 6610 display uses 9-bits NOT 8 to transfer data to the screen. The first bit tells the display whether the following byte is a command (first bit is zero) or some data (first bit is set).

Although Oshonsoft includes built-in software driven SPI routines, I decided to write my own (using the ones in the Arduino library as a base) which send 9-bits in a continuous stream. Because Oshonsoft Basic is so simple to understand, this implementation could be easily ported to a compiler of your choice. The full code listing follows. Below is a summary of how to use the device:

Creating a window:
Put the display into "command mode" (set the first bit of the stream of 9)
Send the command bit followed by 0x2A (command to set the column/x co-ord)
Put the display into "data mode" (clear the first bit of the next stream of 9)
Send the x-start value as a byte (0-131)
Send the x-end value as a byte (0-131, must be greater than x-start)

Put the display into "command mode" (set the first bit of the stream of 9)
Send the command bit followed by 0x2B (command to set the column/y co-ord)
Put the display into "data mode" (clear the first bit of the next stream of 9)
Send the y-start value as a byte (0-131)
Send the y-end value as a byte (0-131, must be greater than y-start)

Put the display into "command mode" (set the first bit of the stream of 9)
Send the command bit followed by 0x2C (command to start writing pixel data)
Put the display into "data mode" (clear the first bit of the next stream of 9)
Send each pixel colour as a byte (assuming 8-bit colour mode)
Repeat for each pixel in the window

Here's the full code for a working PIC project:

Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x83
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

UsbSetVendorId 0x1221
UsbSetProductId 0x1003
UsbSetVersionNumber 0x1122
UsbSetManufacturerString "www.nerdclub.co.uk"
UsbSetProductString "Usb-to-SPI device"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout

AllDigital

Symbol glcd_bl = PORTB.5
Symbol glcd_rst = PORTB.4
Symbol glcd_cs = PORTB.3
Symbol glcd_sda = PORTB.2
Symbol glcd_miso = PORTB.1
Symbol glcd_sck = PORTB.0
Symbol led = PORTA.0

Define SPI_SCK_REG = PORTB 'defines the port where sck line is connected To
Define SPI_SCK_BIT = 0 'defines the pin where sck line is connected To
Define SPI_SDO_REG = PORTB 'defines the port where sdo line is connected To
Define SPI_SDO_BIT = 1 'defines the pin where sdo line is connected To
Define SPI_SDI_REG = PORTB 'defines the port where sdi line is connected To
Define SPI_SDI_BIT = 2 'defines the pin where sdi line is connected To
Define SPI_CS_REG = PORTB 'defines the port where cs line is connected To
Define SPI_CS_BIT = 3 'defines the pin where cs line is connected To

'SDO is not used - but is mapped to MISO on the display
'(serial data out on the MCU to serial data out on the lcd)

Dim spibyte As Byte 'a byte to send to the lcd
Dim spibit As Bit 'first bit is 0=command, 1=data
Dim spiclockdelay As Byte 'how long to wait between spi clock pulses
Dim spiclockus As Bit 'whether clock pulse delay is us or ms

Dim xs As Byte 'x co-ord to start bitmap from
Dim ys As Byte 'y co-ord to start bitmap from
Dim xe As Byte 'x co-ord to draw up to
Dim ye As Byte 'y co-ord to draw up to

Dim i As Byte '}
Dim j As Byte '} general variables
Dim k As Byte '}


init:
  spiclockus = 1
  spiclockdelay = 4
  
  'drive an LED high so we can see what's going on
  High led
  Gosub lcdinitialise

  'drive the led low
  Low led

  UsbStart
  UsbService

loop:
  UsbService
Goto loop

End


sendspi9bits:

  '--------------------------------------------------------
  'FROM THE NOKIA DATASHEET
  'the serial interface is
  'initialized when SCE is High. In this state, SCLK pulses
  'have no effect And no power is consumed by the serial
  'interface. A falling edge On pin sce enables the serial
  'interface And indicates the start of data transmission.
  '--------------------------------------------------------

  'flash the led high
  High led

  Low glcd_sda

  'drive the cs low
  Low glcd_cs
  Gosub spidelay

  'first send the init bit, for either command or data
  If spibit = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  'now clock 8 bits from the spibyte value starting with bit 7
  If spibyte.7 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.6 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.5 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.4 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.3 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.2 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.1 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  If spibyte.0 = 1 Then High glcd_sda Else Low glcd_sda
  Gosub toggleclockpin

  'bring cs high to indicate end of transmission
  High glcd_cs
  Gosub spidelay

  'flash the led off
  Low led
Return


toggleclockpin:
  'take the clock pin high, wait a second, drive it low
  Gosub spidelay
  Low glcd_sck
  Gosub spidelay
  High glcd_sck
Return


spidelay:
  If spiclockus = 1 Then
    WaitUs spiclockdelay
  Else
    WaitMs spiclockdelay
  Endif
Return


lcdinitialise:
  'this is taken from teh pcf8833.c file: Arduino library

  'turn on the backlight
  High glcd_bl
  WaitMs 1

  'send a dummy data byte
  spibyte = 0 'all zeros is interpreted as NOP in the display chip
  Gosub sendcommand

  'send software reset command
  spibyte = 0x01
  Gosub sendcommand
  WaitMs 10

  spibyte = 0x11 'sleepout
  Gosub sendcommand

  spibyte = 0x36 'memory access control
  Gosub sendcommand

  'when a bitmap is drawn to screen, you give a start x, start y
  'and end x and end y co-ordinate. Then drawing can go from left-to-right,
  'up-and-down, or up-and-down, wrapping round to move the pixel left-to-right.
  'For our fonts and so on, we draw each pixel top to bottom, then
  'when we get to the bottom of the column of data, move left to right
  'This is called vertical addressing mode.

  spibyte = 0xe0 'memory access options:
  '1 - my mx v lao rgb x x x
  'my=mirror in Y, mx=mirror in x, V=vertical addressing,
  'lao=line address order, rgb= colour order rgb/bgr
  'e0 came from the Arduino library and is 11100000b
  
  Gosub senddata 'note: data, not command

  spibyte = 0x25 'set contrast
  Gosub sendcommand

  spibyte = 0x40 'contrast value
  Gosub senddata 'note: data=contrast value, not a command!

  WaitMs 10 'allow things to stablise

  spibyte = 0x3a 'set colour mode
  Gosub sendcommand

  spibyte = 0x02 'xxxxx010 = 8-bit colour
  Gosub senddata 'note: data=colour mode value, not a command

  spibyte = 0x00 'dummy command - dunno why, but it's in the C library!
  Gosub sendcommand

  Gosub clearscreen
Return


createwindow:
  spibyte = 0x2a 'set x start and end co-ordinates
  Gosub sendcommand
  
  spibyte = xs
  Gosub senddata

  spibyte = xe
  Gosub senddata

  spibyte = 0x2b 'set y start and end co-ordinates
  Gosub sendcommand
  
  spibyte = ys
  Gosub senddata

  spibyte = ye
  Gosub senddata

Return


clearscreen:

  'turn off the display while the screen is cleared
  spibyte = 0x28
  Gosub sendcommand

  'create a rectangle area the size of the window
  xs = 0
  xe = 131
  ys = 0
  ye = 131
  Gosub createwindow

  'put the display into "write mode"
  spibyte = 0x2c
  Gosub sendcommand

  'fill the rectangle with black pixels
  spibyte = 0x00
  For i = 0 To 131
    For j = 0 To 131
      Gosub senddata
    Next j
  Next i

  spibyte = 0x29 'display on?
  Gosub sendcommand

Return


sendcommand:
  spibit = 0
  Gosub sendspi9bits
Return


senddata:
  spibit = 1
  Gosub sendspi9bits
Return


usbonftin:

Return


usbonftout:

Return


usbonioout:
  'we've received some data on the data i/o (from PC to device)
  i = UsbIoBuffer(0)
  Select Case i
    Case 1 'this is the token to say "send byte"
    If UsbIoBuffer(1) = 1 Then spibit = 1 Else spibit = 0
    spibyte = UsbIoBuffer(2)
    Gosub sendspi9bits


    Case 2 'this is the token to say "set delay time/length"
    If UsbIoBuffer(1) = 1 Then spiclockus = 1 Else spiclockus = 0
    spiclockdelay = UsbIoBuffer(2)

    
    Case 3 'this is the token to say "run the init sequence"
    Gosub lcdinitialise


    Case 4 'this is the token to say "draw a window"
    xs = 10
    xe = 60
    ys = 10
    ys = 60
    Gosub createwindow

    For i = 0 To 20
      For j = 0 To 20
        spibyte = 0
        spibit = 1 '1=data, 0=command
        Gosub sendspi9bits
      Next j
    Next i


    Case 5 'this is the backlight token
    If UsbIoBuffer(1) = 1 Then High glcd_bl Else Low glcd_bl


  EndSelect
Return


usbonioin:
  'we've received an io/data request from the PC (usbioin = send to PC)
  'for some data from the device.
  
Return





Once again, a big thanks to Robot Steve for lending me his Arduino kit while I try to source one of my own!

0

It works! It works!

Posted by Chris on Thursday, July 02, 2009 in , , ,
Victory at last!
Well, not quite, but at least I'm part-way there now!
Yet another GLCD arrived in the post this morning and as soon as I'd shredded it from it's bubble-wrapped envelope, I had a series of pins soldered on to it and had it connected up to my breadboard.

This time it's a LM6038B from Mainline.
A quick read through the datasheet and it looks ok: it's a ribbon-based display ready-mounted on a PCB, with all the nasty charge-pump capacitors and all that nonsense already in place. So you can just shove 3v onto the power/lcd pins and the backlight shares a 0v pin with the display controller, so only one ground connection needed!
The display supports the usual 6800 and 8080 parallel interface, as well as a serial interface. Great. But then I came across this in the datasheet:

1.5 Jumper functions
JP1JP2JP3JP4Interfacemode
closeopencloseclose6800 mode
openclosecloseclose8080 mode
opencloseopencloseserial mode (default)


That's right. Serial mode by default.
The jumpers are actually little solder blobs on the back of the display, so in theory they can be changed. Given that I'd been told that this display used a KS0108-compatible library, it was tempting to give it a go.
But then, I thought rather than screw up yet another display and get no further on, it might be fun/torture to get this working as-is, using the serial interface.

Here's the datasheet
Here's the Oshonsoft PIC18 Basic code to get an 18F2455 mirochip working with the display using PORTA and SPI communication. Simply wire up the microchip pins to the corresponding pins on the display. The only thing to look out for is SPI_SDO - I set this to a pin on PORTB as it's not used for this example - data is sent to the display, not read back from it. Any way, here it is:


AllDigital
'Config PORTA = Output

Symbol glcd_cs1 = PORTA.0
Symbol glcd_res = PORTA.1
Symbol glcd_a0 = PORTA.2
Symbol glcd_sclk = PORTA.3
Symbol glcd_si = PORTA.4

Define SPI_SCK_REG = PORTA 'defines the port where sck line is connected To
Define SPI_SCK_BIT = 3 'defines the pin where sck line is connected To
Define SPI_SDI_REG = PORTA 'defines the port where sdi line is connected To
Define SPI_SDI_BIT = 4 'defines the pin where sdi line is connected To
Define SPI_SDO_REG = PORTB 'defines the port where sdo line is connected To
Define SPI_SDO_BIT = 4 'defines the pin where sdo line is connected To
Define SPI_CS_REG = PORTA 'defines the port where cs line is connected To
Define SPI_CS_BIT = 0 'defines the pin where cs line is connected To

Dim glcdbyte As Byte 'the byte to send to the display
Dim glcdline As Byte 'used to set the current line number of the display
Dim i As Byte 'general use variable
Dim j As Byte
Dim character(6) As Byte 'array of character font slices
Dim charbyte As Byte 'the character to send to the GLCD
Dim ilcd As Byte
Dim jlcd As Byte
Dim klcd As Byte

Dim invertchar As Bit

Config PORTC = Output
High PORTC.2
WaitMs 1000
SPIPrepare

Low PORTC.2
Gosub glcdinititalise
Gosub glcdclearscreen
High PORTC.2

glcdline = 0
Gosub glcdsetline

For i = 0 To 17
 charbyte = LookUp("HOLY CRAP IT WORKS"), i
 Gosub pickcharacter
 Gosub drawcharacter
Next i

End

glcdinititalise:
 'reset pin is pulled low via 6.2k resistor
 'wait for power to stabilise
 WaitMs 100

 'set reset pin high
 High glcd_res
 WaitMs 10

 
 glcdbyte = 0xa2 'LCD bias=1/9
 Gosub glcdsendcommand

 glcdbyte = 0xa0 'no flip on x direction
 Gosub glcdsendcommand

 glcdbyte = 0xc8 'flip on y direction
 Gosub glcdsendcommand

 glcdbyte = 0x40 'init line=0
 Gosub glcdsendcommand

 glcdbyte = 0x2c 'power control voltage convertor on
 Gosub glcdsendcommand
 WaitMs 50

 glcdbyte = 0x2e 'power control voltage regulator on
 Gosub glcdsendcommand
 WaitMs 50
 
 glcdbyte = 0x2f 'power control voltage follower on
 Gosub glcdsendcommand
 WaitMs 50
 
 glcdbyte = 0x25 'regulator resistor select
 Gosub glcdsendcommand

 'contrast control:
 glcdbyte = 0x81 'set reference voltage mode
 Gosub glcdsendcommand
 glcdbyte = 0x20 'set reference voltage resistor
 Gosub glcdsendcommand

 glcdbyte = 0xaf 'turn on the display
 Gosub glcdsendcommand

 glcdbyte = 0xb0 'set page address=0
 Gosub glcdsendcommand


 glcdbyte = 0x10 'set column address upper 4bit=0
 Gosub glcdsendcommand
 glcdbyte = 0x04 'set column address lower 4bit=4
 Gosub glcdsendcommand
 
Return

glcdsenddata:
 SPICSOn
 High glcd_a0
 SPISend glcdbyte
 WaitUs 1
 SPICSOff
Return

glcdsendcommand:
 SPICSOn
 Low glcd_a0
 SPISend glcdbyte
 WaitUs 1
 SPICSOff
Return

glcdclearscreen:
 For glcdline = 0 To 7
  Gosub glcdsetline
  glcdbyte = 0
  For j = 0 To 129
   Gosub glcdsenddata
  Next j
 Next glcdline
Return

glcdsetline:
  glcdbyte = 10110000b
  glcdbyte = glcdbyte + glcdline
  Gosub glcdsendcommand
  glcdbyte = 0x10 'set column address upper 4bit=0
  Gosub glcdsendcommand
  glcdbyte = 0x04 'set column address lower 4bit=4
  Gosub glcdsendcommand
  WaitMs 1
Return

pickcharacter:
 Select Case charbyte

  character(5) = 0 'check for this condition when drawing the letter

  Case 0, 48 '0
  character(0) = 120
  character(1) = 164
  character(2) = 148
  character(3) = 120
  character(4) = 0

  Case 1, 49 '1
  character(0) = 1
  character(1) = 136
  character(2) = 252
  character(3) = 128
  character(4) = 0

  Case 2, 50 '2
  character(0) = 200
  character(1) = 164
  character(2) = 164
  character(3) = 152
  character(4) = 0

  Case 3, 51 '3
  character(0) = 68
  character(1) = 148
  character(2) = 148
  character(3) = 108
  character(4) = 0

  Case 4, 52 '4
  character(0) = 32
  character(1) = 48
  character(2) = 40
  character(3) = 252
  character(4) = 0

  Case 5, 53 '5
  character(0) = 92
  character(1) = 148
  character(2) = 148
  character(3) = 100
  character(4) = 0

  Case 6, 54 '6
  character(0) = 120
  character(1) = 148
  character(2) = 148
  character(3) = 96
  character(4) = 0

  Case 7, 55 '7
  character(0) = 4
  character(1) = 196
  character(2) = 52
  character(3) = 12
  character(4) = 0

  Case 8, 56 '8
  character(0) = 104
  character(1) = 148
  character(2) = 148
  character(3) = 104
  character(4) = 0

  Case 9, 57 '9
  character(0) = 72
  character(1) = 148
  character(2) = 148
  character(3) = 120
  character(4) = 0

  Case 32 'space
  character(0) = 1
  character(1) = 1
  character(2) = 1
  character(3) = 1
  character(4) = 1

  Case 65 'A
  character(0) = 248
  character(1) = 36
  character(2) = 36
  character(3) = 248
  character(4) = 0

  Case 66 'B
  character(0) = 252
  character(1) = 148
  character(2) = 148
  character(3) = 104
  character(4) = 0

  Case 67 'C
  character(0) = 120
  character(1) = 132
  character(2) = 132
  character(3) = 72
  character(4) = 0

  Case 68 'D
  character(0) = 252
  character(1) = 132
  character(2) = 132
  character(3) = 120
  character(4) = 0

  Case 69 'E
  character(0) = 252
  character(1) = 148
  character(2) = 148
  character(3) = 132
  character(4) = 0

  Case 70 'F
  character(0) = 252
  character(1) = 20
  character(2) = 20
  character(3) = 4
  character(4) = 0

  Case 71 'G
  character(0) = 120
  character(1) = 132
  character(2) = 164
  character(3) = 232
  character(4) = 0

  Case 72 'H
  character(0) = 252
  character(1) = 16
  character(2) = 16
  character(3) = 252
  character(4) = 0

  Case 73 'I
  character(0) = 132
  character(1) = 252
  character(2) = 132
  character(3) = 0
  character(4) = 0

  Case 74 'J
  character(0) = 96
  character(1) = 128
  character(2) = 132
  character(3) = 124
  character(4) = 0

  Case 75 'K
  character(0) = 252
  character(1) = 16
  character(2) = 40
  character(3) = 196
  character(4) = 0

  Case 76 'L
  character(0) = 252
  character(1) = 128
  character(2) = 128
  character(3) = 128
  character(4) = 0

  Case 77 'M
  character(0) = 252
  character(1) = 8
  character(2) = 48
  character(3) = 8
  character(4) = 252

  Case 78 'N
  character(0) = 252
  character(1) = 8
  character(2) = 16
  character(3) = 252
  character(4) = 0

  Case 79 'O
  character(0) = 120
  character(1) = 132
  character(2) = 132
  character(3) = 120
  character(4) = 0

  Case 80 'P
  character(0) = 252
  character(1) = 36
  character(2) = 36
  character(3) = 24
  character(4) = 0

  Case 81 'Q
  character(0) = 120
  character(1) = 132
  character(2) = 164
  character(3) = 120
  character(4) = 128

  Case 82 'R
  character(0) = 252
  character(1) = 36
  character(2) = 36
  character(3) = 216
  character(4) = 0

  Case 83 'S
  character(0) = 72
  character(1) = 148
  character(2) = 164
  character(3) = 72
  character(4) = 0

  Case 84 'T
  character(0) = 4
  character(1) = 252
  character(2) = 4
  character(3) = 0
  character(4) = 0

  Case 85 'U
  character(0) = 124
  character(1) = 128
  character(2) = 128
  character(3) = 252
  character(4) = 0

  Case 86 'V
  character(0) = 28
  character(1) = 96
  character(2) = 128
  character(3) = 96
  character(4) = 28

  Case 87 'W
  character(0) = 252
  character(1) = 64
  character(2) = 48
  character(3) = 64
  character(4) = 252

  Case 88 'X
  character(0) = 196
  character(1) = 40
  character(2) = 16
  character(3) = 40
  character(4) = 196

  Case 89 'Y
  character(0) = 76
  character(1) = 144
  character(2) = 144
  character(3) = 124
  character(4) = 0

  Case 90 'Z
  character(0) = 196
  character(1) = 164
  character(2) = 148
  character(3) = 140
  character(4) = 0

  Case 91 'up/down
  character(0) = 0
  character(1) = 0
  character(2) = 80
  character(3) = 216
  character(4) = 80

  Case 92 'sand saver
  character(0) = 0
  character(1) = 0
  character(2) = 144
  character(3) = 168
  character(4) = 72

  Case 93 'arrow
  character(3) = 1
  character(0) = 248
  character(1) = 112
  character(2) = 32
  character(4) = 0

  Case 94 'hyphen
  character(0) = 0
  character(1) = 16
  character(2) = 16
  character(3) = 16
  character(4) = 0

  Case 58 ':
  character(0) = 1
  character(1) = 216
  character(2) = 216
  character(3) = 0
  character(4) = 0

  Case 47 '/
  character(0) = 192
  character(1) = 48
  character(2) = 12
  character(3) = 0
  character(4) = 0

  Case 62 '>
  character(0) = 124
  character(1) = 56
  character(2) = 16
  character(3) = 0
  character(4) = 0

  Case 40 '(
  character(0) = 0
  character(1) = 0
  character(2) = 120
  character(3) = 132
  character(4) = 0

  Case 41 ')
  character(0) = 132
  character(1) = 120
  character(2) = 0
  character(3) = 0
  character(4) = 0

  Case Else 'turn into a space
  character(0) = 1
  character(1) = 1
  character(2) = 1
  character(3) = 1
  character(4) = 1

 EndSelect

Return

drawcharacter:
 'at this point, we should have: charbyte containing the character index
 'which has been "passed" into pickcharacter which has populated the
 'array called "character" - 5 pixels wide, possibly more
 'so we keep sending each byte to the GLCD until we hit a sequence
 'of two empty (zero) bytes

 ilcd = 0
 jlcd = 0

 While ilcd < 5

  klcd = character(ilcd)
  If invertchar = 1 Then klcd = klcd Xor 255

  'send one byte (column of 8 pixels) to the display
  If character(ilcd) = 1 Then
   'this is the special character to say "blank"
   If invertchar = 1 Then
    glcdbyte = 255
   Else
    glcdbyte = 0
   Endif
  Else
   glcdbyte = klcd
  Endif
  Gosub glcdsenddata

  'move on to the next column of data within the character
  ilcd = ilcd + 1
 
 Wend
Return




Here's the end result:



Now if only there were some way I could keep this and use a second set of serial (SPI) lines to send/receive data to the eeprom chip. That'd be a massive leap forward....

0

Nil Satis Nisi Optimum

Posted by Chris on Tuesday, June 09, 2009 in , , , , ,
Never satisfied until the best. Or something like that. When you've grown up with a die-hard blue-nosed Everton fan, stuff like that tends to stick in your head and pop out at the most inappropriate moments. Perhaps today it's not quite so inappropriate.

After all, the ScoreStore Game Keeping Device (see, it has a name now, thanks to Robot Steve over at BotBuilder.co.uk) is done, finished, working, tested, working, done, finished. But somehow, I feel it's not quite there...

For a start, the LCD display is big and clumsy. Secondly, it needs more than 2xAAA batteries to power it. Thinking about the enclosure - which has yet to be designed - this is going to make the device quite a big heavy thing to carry around in your pocket! A big display I can live with, but having to pump 4 x AAA batteries into something like this seems a bit overkill - a portable CD player or a torch, maybe, but an LCD display with a couple of buttons?
My game score keeping device will end up the size of a C90 cassette tape! It'd be easier to carry an A5 notebook in your pocket - there has to be some way of making the whole thing smaller, lighter, and altogether more "professional" looking.

As it turns out, this little thing appeared on eBay. It's a GLCD (graphical LCD). Look at it - it's tiny. No thicker than an After Eight mint. And, best of all, it runs at 3v (as low as 2.5v, so it still works even after a bit of battery drain!). Brilliant.


Low profile, low voltage, and 4 times bigger than a 16x2 character display.
What's not to love about this GLCD?


Luckily the Oshonsoft PIC18 simulator/compiler supports graphical LCD displays - so the commands currently used for writing data out to the 16x2 LCD can be quickly and easily upgraded to use the GLCD. And, to put the cherry on the cake, we can have some nice icons and monochrome bitmaps, just to show off how clever it all is!

0

I2C random access and streaming

Posted by Chris on Sunday, June 07, 2009 in , , , ,
Here's some code showing how to read/write to an eeprom using byte-addressing and Oshonsoft Basic. It was written for an 18f2455 running at 20Mhz and a 24C256 eeprom using I2C communication:


Dim addr As Word
Dim writebyte As Byte

Define I2CCLOCK_STRETCH = 9
Symbol sda = PORTB.3
Symbol scl = PORTB.4

addr=32
writebyte=100
Gosub writeI2C

End


writei2c:
  I2CWrite sda, scl, 0xa0, addr, writebyte
  addr = addr + 1
  WaitMs 5
Return



The key thing to note here is the "stretch" command and the 5ms delay after each write instruction. According to the datasheet, a delay of 5ms is needed to allow the chip to finish writing its data to the addressed memory location. In practice, this is a little bit long - you can get away with 4ms and some people have reported delays of 3ms or less. Others have also said it's possible to overclock these eeprom chips and have them running at 1Mhz or more.

I found that 4ms delay works, but to be extra-sure, I've gone for a 5ms delay after writing data. When reading data back from the eeprom, the delay is not necessary.

To use the code above, simply put a 2-byte address into the variable "addr".
Put a value into the variable "writebyte"
Call the sub-routine "writeI2C" using the gosub command.

Equally as important are the connections to your eeprom chip.
Leaving lines floating (ie. not tied to either power or ground) means really erratic behaviour with some read/write operations working and others failing. Here are the pin connections I used:

pin 1-3 (A0-A2): ground
pin 4: Vss : ground
pin 5: Vcc : 5V
pin 6: WP : 0v (write protect)
pin 7: SCL: to PIC pin (RB4)
pin 8: SDA: to PIC pin (RB3)

0

Oshonsoft Special Offer for RobotBrighton members

Posted by Chris on Tuesday, May 19, 2009 in , ,
Anyone like messing about with robots?
Anyone live in/near Brighton?
Anyone a member of the RobotBrighton group?

Vladimir Soso (author of the PIC simulator and basic compilers software) has put together not one, not two, but THREE fantastic special deals for all members of RobotBrighton.

He's offering the PIC simulator for just €18 instead of 29. That's nearly 40% off! And if you're serious about PIC programming, you can't miss the amazing PIC18 + USB bundle he's put together for just €45.

Check out the email below from Vlad and grab yourself a bargain....



Dear members of RobotBrighton,

For a limited period of time, you can take advantage
of the following special offers to get the licenses for
OshonSoft.com software at convenient prices.

Special Offer #1 (18 euros):
- PIC Simulator IDE personal license
- PIC basic compiler 32-bit math support
- PIC basic compiler structured language support
Online payment can be completed by following the link:
http://www.oshonsoft.com/directorder.php?pid=30

Special Offer #2 (36 euros):
- PIC Simulator IDE personal license
- PIC basic compiler 32-bit math support
- PIC basic compiler structured language support
- PIC18 Simulator IDE personal license
- PIC18 basic compiler 32-bit math support
- PIC18 basic compiler structured language support
- PIC10F Simulator IDE personal license
Online payment can be completed by following the link:
http://www.oshonsoft.com/directorder.php?pid=31

Special Offer #3 (45 euros):
- PIC18 basic compiler USB support personal license
- PIC Simulator IDE personal license
- PIC basic compiler 32-bit math support
- PIC basic compiler structured language support
- PIC18 Simulator IDE personal license
- PIC18 basic compiler 32-bit math support
- PIC18 basic compiler structured language support
- PIC10F Simulator IDE personal license
Online payment can be completed by following the link:
http://www.oshonsoft.com/directorder.php?pid=32

These special offers will expire on June 9, 2009.

Best regards,
Vladimir
OshonSoft.com


0

Multi-channel servo controller

Posted by Chris on Sunday, May 10, 2009 in , , ,
So here's the first working version of multi-channel, USB (and serial) driven servo controller. It's by no means complete, and for some reason, my servos only move 45 degrees either side of the zero point but the theory behind it seems to work.

Here's what happens, in a nutshell:
Unlike other multiple servo controllers, this one does not activate the servo pins in sequence (which, incidentally on a 20Mhz crystal limits you to 8 servos, maximum).
This controller switches on all servo pins at the same time, and uses timer1 to decide when each servo pin should be switched off.
Since all pins are on for the first 1ms, and are turned off during the second millisecond, this keeps 3-20ms (9/10ths) of the processing time free for data processing and other tasks (give or take a few nanoseconds for some basic time-keeping checks).

Using the same layout as my earlier USB project you can get some results quite quickly. Set your board out according to the schematic and drop the code (below) onto the PIC18F2455.

Attaching your servo signal wire to pin RB0 should move the servo to the zero position. RB1 should move the servo to one extreme, pin RB2 to the other. (in fact, if you look at the code, you should see that pins RB3-7 also move the servo to the same angle as RB1.

I'm still working on fine-tuning the code and experimenting with other servos.
I'm using a Hextronic HXT900 servo which doesn't appear to move through the full 180 degrees. The half-way signal does appear to put the servo into the centre position. However, when sending signals to move the servo to its extremes, it only moves 45 degrees in each direction, rather than the full 90.
I'm not sure whether this is a software issue, or whether it's particular to this servo model. Results of any further investigations will be posted here in time!

Oshonsoft PICBasic listing for 18F2445

Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x81
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

'PORTA.0 is feature LED
'PORTA.1 is i/o LED
'these can be removed if necessary

declarations:
Dim ax As Byte
Dim i As Byte
Dim j As Byte
Dim t As Byte
Dim svh(16) As Byte
Dim svl(16) As Byte

usbcrap:
UsbSetVendorId 0x1220
UsbSetProductId 0x1234
UsbSetVersionNumber 0x1122
UsbSetManufacturerString "www.nerdclub.co.uk"
UsbSetProductString "Multiple servo USB controller"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout

init:
AllDigital
Config PORTA = Output
Config PORTB = Output
T1CON = 0 'set all bits to zero

startusb:
PORTA.0 = 1 'turn on an output so we can see the pic is running
UsbStart
PORTA.0 = 0 'turn off output once USB has set up

startup:
'set the value for timer1 to count up to
'(count to 5000 = 1ms: highbyte=5000/256, lowbyte=5000 mod 256)
CCPR1H = 19
CCPR1L = 136

'to allow a 0-100% range, sv(0-15) can take the value 0-100
'(we multiply these up by 50 as the values are received, to give
'a range of 0-5000, which is 0ms to 1ms).
'these are some example values to test the middle and extreme
'ranges of the servo controller on pins 0,1,2

svh(0) = 9
svl(0) = 196

svh(1) = 0
svl(1) = 0

svh(2) = 19
svl(2) = 136

'1011 = special event interrupt, clear timer etc.
CCP1CON.CCP1M3 = 1
CCP1CON.CCP1M2 = 0
CCP1CON.CCP1M1 = 1
CCP1CON.CCP1M0 = 1

'PIR1.CCP1IF = 0 'clear any "bogus" reading
T1CON.TMR1ON = 1 'enable timer1

t = 0

loop:

  If t = 1 Then
    'turn on all servo outputs for 1ms
    PORTB = 0xff
    PORTA.0 = 1 'led to show timer is running
  Else
      If t = 2 Then
      'this is where outputs get turned off. Because there may
      'be a delay in processing this loop, we compare the actual
      'value held in Timer2 and if it's greater than our servo-off
      'value (0-100% multipled by 50) then turn that pin off

      If TMR1H > svh(0) Then PORTB.0 = 0
      If TMR1H = svh(0) And TMR1L >= svl(0) Then PORTB.0 = 0

      If TMR1H > svh(1) Then PORTB.1 = 0
      If TMR1H = svh(1) And TMR1L >= svl(1) Then PORTB.1 = 0

      If TMR1H > svh(2) Then PORTB.2 = 0
      If TMR1H = svh(2) And TMR1L >= svl(2) Then PORTB.2 = 0

    Else
      'turn off the servo outputs for the duration 3ms to 14ms (and 0ms-1ms)
      PORTB = 0x00
      PORTA.0 = 0 'turn off timer led

      'service the USB port for data
      UsbService

      'check to see if we've had two bytes of serial data
      If i = 0 Then
        'Hserget i
      Else
        If j = 0 Then
          'Hserget j
        Else
          'parse the two byte instruction and update the servo-off
          'values as necessary, then reset i and j to zero
          i = 0
          j = 0
        Endif

      Endif
    Endif
  Endif

   'check for CCP1 interrupt flag (even if interrupts are not
   'on, you can poll this flag to see if an event has occurred)
   'this really needs to be in an interrupt routine so that the timer
   'is kept as up-to-date as possible
   If PIR1.CCP1IF = 1 Then
     t = t + 1
     PIR1.CCP1IF = 0

     If t > 14 Then
       t = 0

       'reset the timer to exactly zero
       '(so if there's been a delay switching on, we're back in sync
       'when it comes to switching the signal off, otherwise there
       'might be a bit of judder as the time period between on and off
       'may differ by a few thousandths of a millisecond)
       TMR1L = 0
     Endif

   Endif

Goto loop

End


usbonftout:
Toggle PORTA.0
Return


usbonftin:
'we've received some data in the feature report
Return


usbonioout:
Toggle PORTA.1
Return


usbonioin:
'we've received some data on the data i/o, so
'update the necessary servo high and low byte values
'to get the selected servo to move to its new position

Return


0

Blog posts are just like buses...

Posted by Chris on Saturday, May 09, 2009 in , , , , , ,
...you wait for ages for one, then three come along at once.
Just a quick note to say that, despite there being no blog updates for a while, it's not all ground to a halt here at Nerd Towers.

After the success (mostly) of the USB relay switching device I've been inspired to help out with a project for Steve at www.botbuilder.co.uk, which is a USB-driven multiple-servo controller.

Simply put, to drive a servo, you need to send a signal pulse to the servo every 20ms or so (50hz) the duration of which must be between 1ms and 2ms.
A 1ms signal means "0 degrees" (or -90 if your zero position is half way between the two extremes). To move the servo to 90 degrees (or its mid-point) a signal of exactly 1.5ms must be sent, and a signal of 2ms represents "fully extended" or "180 degrees" (or 90 degrees if you're working from -90 to 0 to 90).

Many PICs have a PWM (pulse-wave-mode) module built into them, and some even have two or more. But very few (if any) can support up to 16 servos.
Allowing for errors, and a signal pulse time of 2.5ms per servo - remembering that each servo must have a signal sent to it every 20ms at most - the maximum number of servos that a PIC can control, serially, is 8.
(servo 1 takes 2.5ms, followed by servo 2, another 2.5ms, followed by servo 3 etc...)

8 x 2.5 = 20m/s.

And on top of all this, your PIC is running at 100% capacity - there's no room in there for receiving data requests to update the position of the servos.
So I'm trying to recreate PWM using an 18F series PIC, but using timer1 to create interrupts to drive all servo pins high at the same time, then use some form of offsetting to drive them low again after a period of time between 1ms and 2ms.

Look out for some Oshonsoft PICBasic code shortly (if I get it working!)

0

Would you Adam and Eve it?

Posted by Chris on Friday, April 24, 2009 in , , , ,
After attending the Robot Brighton Shadow Robot Company Talk I had just a few minutes from getting home to retiring to bed (it was gone midnight after all!).
I'd asked around and managed to blag a 470nF capacitor from Kind Steve (and borrowed a PIC programmer from him, just in case mine was playing up: I still didn't know if it was burning hex files onto the 18F series of chips properly) and thought I'd shove it into my breadboard and see what happened.

As I pulled it from it's little plastic bag, I realised he'd given me a 470uF not a 470nF capacitor. It was a big black cylindrical thing, not the little round disk I'd been expecting.

I was about to call it all off and go to bed, when I decided that there'd be no harm in giving it a try anyway, what with him having taken the time to dig one out and bring it with him. So I did. And then plugged in the USB cable.

Nothing happened.
And then this came up:



Wahoo! I finally had a native USB device working!
And just to make sure, I ran the test app that came with the PIC 18 simlulator called hidterminal.
What is does is generate a random 8-byte string and send it to the device.
If you read through the sample code, you'll see that it decreases each value in each byte of the feature code by one, just before the data is sent back to the host (and increases the i/o report by one). From the screenshot below, you can actually see this in action:


The vendor ID was changed to 1221 just to see that a USB device with
any VID/PID combination would be recognised


And all this without having to use a different programmer.
It seems that mine does work with 18F chips, using DonkeyProg after all.
Here's the final schematic for anyone trying to get a native USB device working from a PIC18F2455 controller - don't skimp on any of the parts: they're all critical!



How exciting!
Tomorrow I might just lay out a PCB for it....

0

I got myself a crying, waking, sleeping, talking 18F2455

Posted by Chris on Monday, April 20, 2009 in , , ,
Yahoo! My 18F2455 chips arrived in the post this morning (8am can you believe?!)

And, ok, maybe they're not quite that sophisticated, with all the crying, walking stuff. To be honest, not one of them cries or walks or sleeps or talks. In fact not one of them does anything. Because I haven't programmed one yet.

The first thing I did was find out what type of settings I needed to use for the programmer and found that the internal oscillator for the 18F2455 needs to run at 6Mhz (or 48Mhz) if you're going to use USB. No problems so far - at least it has an internal oscillator. Then I read the spec for the USB port and it says:
"With PIC18F2455/2550/4455/4550 devices, the primary oscillator becomes part of the USB module and cannot be associated to any other clock source. Thus, the USB module must be clocked from the primary clock source; however, the microcontroller core and other peripherals can be separately clocked from the secondary or internal oscillators as before."

So whether you like it or not, you have to use an external crystal if you want to make use of the USB comms. The internal oscillator is either dedicated to the USB timing or the micro clock source - one or the other. Without USB, it is worth noting, that these chips have a wide range of internal oscillator values available - but if you're not using the USB comms, why use one of these chips and not some cheaper alternative??

Added into the mix is this statement:
"When the PIC18F4550 is used for USB connectivity, it must have either a 6 MHz or 48 MHz clock for USB operation, depending on whether Low-Speed or Full-Speed mode is being used....The USB clock for Low-Speed mode is derived from the primary oscillator chain and not directly from the PLL. It is divided by 4 to produce the actual 6 MHz clock. Because of this, the microcontroller can only use a clock frequency of 24 MHz when the USB module is active and the controller clock source is one of the primary oscillator modes (XT, HS or EC, with or without the PLL). This restriction does not apply if the microcontroller clock source is the secondary oscillator or internal oscillator block."
What does all that mean? I'm not too sure.
According to the USB look-up table, it is possible to use a 20Mhz crystal (these are the only ones currently in stock in Brighton's Maplins) as an external clock source and still have access to USB functionality. I think that a bit (or rather quite a lot) of trial and error will be needed here....

So I've now got a chip in my programmer, ready to try out the USB code from the Oshonsoft website (which incidentally behaves peculiarly in the simulator - it keeps jumping back to the start of the code after executing the UsbStart command) but have no way of testing it without YET ANOTHER trip to Maplins for a new crystal

whos.amung.us

Copyright © 2009 .Nerd Club All rights reserved. Theme by Laptop Geek. | Bloggerized by FalconHive Supported by Blogger Templates.