2

QVGA progress

Posted by Chris on Sunday, November 29, 2009
It seemed pretty obvious, we'd got a rectangle drawn on screen, we'd got some characters drawn on the screen, the next logical step was to put some characters on top of a rectangle. Drawn correctly, a few lines of characters drawn over a rectangle could look just like a menu.



How about that then?
A few tweaks to the current PIC code, and we're now able to draw menus, with highlighted items. In fact, we've pretty much built a menu-drawing "library", allowing menus to be drawn anywhere on the screen, any size, with any number of entries (as many as the screen will allow at least).
We've also fixed the bug when drawing characters, so they all have lovely nice outlines around them and can be drawn on any coloured background.

To date, we've got a procedure which takes the following variables and draws a working menu:
menu_x
menu_yco-ordinates on the screen to draw the menu at
menu_width
menu_heightthe background rectangle of a menu can be set to any size
backcolourcolour of the background rectangle
forecolourfont colour drawn over the menu rectangle
menubar_highlightcolour of the "stripe" to draw on the highlighted menu item

When it's hooked up to eeprom, we'll be able to add in stuff like "what to do when a menu item is selected". But that will have to wait - as soon as the Flash eeprom is hooked up, we've got bitmaps to draw.......

UPDATE: Check out our YouTube video showing the menu in action:
http://www.youtube.com/watch?v=sBe5wHwwa0c

2

Progress so far

Posted by Chris on Friday, November 27, 2009
Well, whenever you get any graphical screen working, the first job is always to write a repeating message - "Nerds are cool" or something like that.
And to display a message on a graphical screen, you need to have a "bitmap" font - a sequence of bytes that, when sent to the display, create letters on screen.

We've chosen the "BlackoutX" font as it has a thick outline around each character, making it ideal for dark and light backgrounds. It also happens to be a fixed width font (always easier for implementing bitmap-based text) and even better, each character is 8x8 pixels in size. It's just like a custom ZX Spectrum font!

Note to uber-nerds and ZX Spectrum bores: poke 23606,x and poke 23607,y where x and y are the high/low bytes pointing to the start address where your character set has been poked into memory. It's amazing what you remember, even decades later....


The outline around each of the characters also mean that this font would be equally suitable on a dark or light background, or even drawn directly over a bitmap/image.



I've deliberately only coded a few letters, to keep the size of the code down, so burning it to the mcu doesn't take forever each time I try out an update.
I have coded the entire font set (I wrote a custom VB app to do it for me) but the full font set bloats the PIC code from 3k to 12k - the burning time increases fourfold and while I'm still trying stuff out, I only need a few characters anyway. Implementing other characters means simply increasing a look-up table.

And now for the "reveal" - a working demo of the BlackoutX font on a QVGA LCD screen:



Ok, it's not quite there yet, but at least you can see readable characters on the display! There's a little bug in the code that draws the "transparent" pixels as black, which make the letters appear to be on a black background - so you don't get the full "outline" effect. But that shouldn't take long to hunt down and sort out....

0

Check this out!

Posted by Chris on Thursday, November 26, 2009
Result.
Success.
Call it what you will.



See that little blue square?
I drew that. It seems the LCD init routine was working all along - it just wasn't visible because of the backlight problem! Put 9V onto the backlight (check out the PP3 connected to the VIN pin at the top of the photo) and it all works perfectly.

So at least have a working solution for now, keeping the PP3 connected to make the LCD viewable. Obviously, in time, we need to be able to get the whole thing running from a single 3.3V source. But that's a problem for tomorrow. Today, we've got some graphics drawing libraries to build!

0

Getting the same results at last!

Posted by Chris on Thursday, November 26, 2009
It's been a despondent couple of days in Nerd Towers, trying to get the LCD display working from a PIC 18LF4550 (with USB support). First off, we had to step the supply voltage down from 5V (from the usb bus) to 3.3V via voltage regulator. This is to simulate running the screen and PIC from a lipo battery, not from the usb bus.

The problems started straight away.
Maplins Brighton didn't stock any 3V3 regulators. The web said "2 in stock" but at the store, they had none. Now, I don't mind admitting that Wrexham in North Wales is not the nicest place to live, but the Maplins store there is excellent. They stock pretty much anything you care to ask for over the components counter, and the staff and friendly and knowledgeable. Ask for a 470uF capacitor and they'll even ask if you're building a USB device (they take in interest in what their customers are using the components for) and give you a range to choose from - they're interested in what they sell and understand their customers.
The Brighton Maplins stock nothing. Two NPN transistors? What's the catalogue number? Sorry, we've only got one of those in stock. No I can't offer an alternative. You give me a catalogue number and I'll see if we've got it in stock (they invariably haven't!)

Anyway, enough about Maplins - a load of 3v3 regulators arrived in the post (ordered from the Farnell website, delivered next day) and we got to work. We made sure that we had the 18LF versions of microchips (the L being "low voltage") which run at anywhere between 2.5V and 5.5V.
Running off the 5V USB bus, the PICs worked fine (we always use an LED to show when the PIC is doing something, so as long as it's lit/flickering, we can tell that the PIC is actually doing something). But run a PIC18LF4550 from 3.3V and nothing happens. Zero. Zilch.

It turns out that PIC18s can't be relied on to support 20Mhz crystals at 3.3V - the datasheet shows a voltage/oscillation graph and 20Mhz is just out of the "safe zone" at 3V3. So we had to buy in some 16Mhz crystals (yes, you guessed it, none in stock at Maplins, Brighton). While waiting for them to arrive, I stuck some wires onto the crystal on Xing's Arduino board and used that (the Arduino uses a 16Mhz crystal - perhaps for the same reason?).

Still nothing.
What this time?
The brown-out fuse! Even with the brown-out voltage set to 2.1V the PIC refused to work. Disable the brown-out fuse, and the PIC magically comes to life and works perfectly with a 16Mhz crystal (albeit by this time, we'd reduced the code to a simple "turn-on-an-led" routine).

So now we had a PIC running at 16Mhz and at 3.3V. Brilliant.
Code it up with the LCD initialisation routines and off we go.....
Nothing.

Plug the LCD into the Arduino - it lights up and initialises perfectly.
So at least we know we've not damaged the LCD!
Plug the LCD into the PIC and there's nothing. Zero.
Testing the pins with a multimeter, comparing the two set-ups provided us with some interesting information. Namely, that when connected to the Arduino (see photo on this earlier post) the VIN pin has 9V on it. When connected to the PIC, this pin reads 0V.
Xing specifically said to connect 3.3V to the 5V supply pin and NOT to use the VIN pin (because of the way this prototype board is wired, the 5V supply on the Arduino does actually read 3.3V). So when connected to the PIC, the VIN pin is zero.
What happens if we bend this pin back, so it can't make a connection when the LCD is connected to the Arduino?
Hmmmm.
The LCD fails to light up. It just sits there. Dead.

Result! It may not be working, but at least we now get the same result using both PIC and Arduino controllers.

0

Now we're getting somewhere....

Posted by Chris on Tuesday, November 24, 2009
Back from holidays - ok, not all the nerds went away, but not everyone is quite as dedicated to filling the blog with drivel as some of us, so things were on hold for a while. But the good news is, we're back!

And the even better news is that the day after we got back, we'd already arranged a meeting with our old pal Xing from NuElectronics. And what better place to meet up and sort out LCDs, charge-pump capacitors and all things techie, than in a busy cafe on Clapham Junction Station, at the height of rush-hour? No, we couldn't think of one either, so that's where we met up!

It's always nice to meet up with people and put a face to name (not all us nerds are socially inept, though sometimes it does feel that way!) and in no time at all, two and a half hours had flown by. Xing had some really good ideas for putting our ScoreSure device into production, including having "kits" made up, consisting of enclosure, boards, batteries and so on, ready to be programmed and assembled. We also discussed using a bootloader and the pros and cons of allowing end users to install their own firmware updates, and talked at some length about the future of ScoreSure even though we've yet to build a final, working prototype! In short, it was a really interesting and exciting meeting, and he has given us a deadline to work towards - he's away visiting factories and suppliers in China in December, so we've got just a few short weeks to get something designed and built, ready for prototype manufacturing.

At the end of the meeting, he presented us with a working 320x240 QVGA LCD, mounted on an Arduino board. We'd sent him one of our displays, on our own breakout board to see if the problem we'd had was the hardware (dodgy soldering/etching) or the software in the PIC. He has taken our screen off the dodgy breakout board, and put it onto one of his kits just to make sure that it actually worked (he did say he couldn't find a fault on our board, but neither could he get it working with his set-up either). He even went to the trouble of loading example code into the Arduino to demonstrate it working with our dodgy screen:



So now we have, in our grubby little mitts, a working LCD board mounted on a breakout board, ready to interface with our own PIC microcontrollers. Just as soon as we've worked out which voltages go to which pins, I'm sure it won't be long before the SureScore logo makes and appearance on a full-colour QVGA display.......

0

En vacances

Posted by Chris on Thursday, November 19, 2009
Nous sommes en vacances.

1

Check out the working QVGA LCD!

Posted by Chris on Tuesday, November 17, 2009
Look at this beautiful image. A working QVGA LCD. Neat huh?
Look closely. Yes, it's a mirror image, but that's only due to a few flags not being set. That's easy to resolve.



Now look again. Imagine it was drawn the right way around. Testing blah blah... with an AVR. That's right. An AVR. Until now, we've worked exclusively with PICs.
In fact, we've an admission to make - that's not actually one of our screens that's working: it's one that Xing at NuElectronics got working and sent us photos to show that it is possible!

But we've got our hands on the correct datasheets this time (at least, the same ones he's been using, so they should be correct) so it shouldn't be too long before we're looking at our own bitmaps on these excellent little screens.....

0

Bit padding and byte stuffing

Posted by Chris on Saturday, November 14, 2009
Determined not to let the fact that the LCD still refuses to switch on stop us, we've been pretty busy over the last few days. Ok, the LCD just sits there, blank and lifeless. But one day it will flicker into life. And when it does, we need to be able to send data to it. So we'll just continue, unabashed, as if everything was working anyway.

So now we're looking at data storage and sending data to the LCD.
Our preferred method is through a 16-bit parallel data bus. The LCD typically uses 18-bit colours for the display, but internally can convert a 5-bit colour value into 6-bits for the red and blue channels (it simply copies the most significant bit into the least significant bit place).



This means that we need a piece of software that can take 24-bit images and convert them to 16-bit. That's easy enough. But we need to make sure that said software stores the data in R5-G6-B5 format. Hmmm. Not quite so easy.

Then we started to think about numbers - a 320x240 image takes up 76,800 bytes. Since we're using 16-bit colour values (i.e. two bytes per pixel) this means a whopping 153,600 bytes for a full screen image. Wow!
So we've opted to use "palletized" images - a bit like GIFs: create a palette of 256 colours, then use a single-byte-per-pixel to point to the palette index. This will keep full-screen images down to *just* 76,800 bytes (plus 512 bytes for the palette).
Storing such an image in Flash memory means taking up about 302 "pages" of data (one page = 256 bytes). And our 4Mbit memory chip has 2,048 pages, so each full-screen image will take up just under 15% of the available memory! It's probably best to keep the number of full screen bitmaps down to a minimum!

We've discussed sprite/tile-based approaches and will investigate these further, but for now, we're concentrating on storing and displaying a full-screen image, in R5-G6-B5 format. So here we go:

Each pixel in the source image is interrogated and it's full colour (24-bit) value is retrieved. This colour value is split into its red, green and blue components (we use the fantastic Freeimage DLL for all our image manipulation tasks.)

Each red, green and blue element of the pixel colour is a single byte (8-bits).
We want a 5-bit value for red, a 6-bit value for green and a five-bit value for blue. To achieve this, we simply bit-shift the red value RIGHT three times (the same can be achieved by dividing by 2, three times, ignoring any remainder values). So a red value of, for example, 186 is binary 10111010. We can see visually that bit-shifting this three positions to the right should give us xxx10111.
Divide by two, three times (or divide by 2^3 = 8) and we get:
186 / 2 = 93
93 / 2 = 46 (ignore the 0.5)
46 / 2 = 23.

The binary representation of 23 is 10111. Perfect!
(if you're not confident with decimal to binary conversions, a quick Google search should get you started, or try this link.)

We repeat this process for the blue element (bit-shift right three places)
For green, for only need to bit-shift two places right (or divide by 4) since we want to keep 6 bits for the green channel. (Why six? Because the LCD specification says a 16-bit interface is mapped to a R5-G6-B5 colour value).

If you look closely at the RGB breakdown (above), you'll also see that the green 6-bit value is split over the two bytes. Bugger.
So we need the 5-bit red value to occupy the upper 5 bits of the first byte, followed by the upper three bits of the green colour. Now listen up, this is where it gets tricky: We want the lower three bits of the green colour to occupy the upper three bits of the second byte. Then the five bits that make up the blue colour to occupy the lower five bits of the second byte. Got that?

Here's what we do:
Bit shift LEFT the red colour value, three times
(yes, we could have just used bit-masking earlier, but I'm trying explain a process here, so bear with it!). To shift right, we divide by 2. It makes sense that to bit-shift left we multiply by two:

The red value xxx10111 becomes 10111000

Let's say we have a green value 42, binary xx101010
We want the first three bits to take the place of the last three zeros in the red colour value. How do we do this? As before, bit-shift right, three-times.
Our green value xx101010 becomes xxxxx101
Now simply add this value to the red value.
For bit-wizards, you can perform an OR operation on the two binary values, the result is the same:

10111000 OR 00000101 = 10111101
184 + 5 = 189
10111101 = 189. Perfect!

The easiest way to get the last three bits of the green value is to use bit-masking.
Simply put, bit-masking compares two values, on a binary level, and where two bits are both set, the resulting bit is set (value=1) otherwise is not set (value=zero).
Here's an example:

1011 AND 01110

first bit of first value: 1, first bit of second value: 0, result = 0
second bit of first value: 0, second bit of second value: 1, result = 0
third bit of first value: 1, third bit of second value: 1, result = 1
fourth bit of first value: 1, fourth bit of second value: 0, result = 0

So 1001 AND 01110 = 0010

If we want to recover just the last three bits of a value, we create a "mask" made up of the bits we want to keep. You might even call it a "bit-mask". The bitmask for the last three digits is 00000111

So, back to our RGB colours - we want the last three bits of the original green value 42, binary xx101010:

00101010 AND 00000111 = 00000010

The first three bits (the ones we're interested in) are: xxxxx010
But we need these three bits to be the first three bits in the second byte value.
This means, as before, bit-shifting LEFT, five-times (multiply by 2, five times- or multiply by 32)

We can see that bit-shifting the value left should give us 01000000
By a happy coincidence, the original value 010 = 2
2 x 2^5 = 64
The binary representation of 64 is 01000000. Perfect!

So we now have the lower three bits of our original green value in the upper three bits of the second byte. All that remains to do is add in the blue bits. As before, a bitwise OR statement would achieve this - or we could simply add the blue value to this new second byte value.

Let's say our blue value is 11, binary 01011.
01000000 OR xxx01011 = 01001011
64 + 11 = 75
75 in binary = 01001011

Right. That's one 24-bit pixel stuffed into a 16-bit (2-byte) value.
Only 76,799 more to go!

0

Connecting to a QVGA LCD

Posted by Chris on Thursday, November 12, 2009
Well yesterday morning a parcel arrived containing one of these little photo frame jobbies. We plugged it in and tried it out - very nice. The image quality is great (though the refresh rate looked a little disappointing - you could visibly see the screen updating, and it took 1-2 seconds to draw an entire full-screen image).
No matter, what we needed to do was crack the thing open and get stuck in, just like Xing had done with his, in order to send us these photos.

Once it was open, the first thing to do was to get the LCD ribbon off the PCB.
This was actually easier than first anticipated, following the sage advice of Robot Steve. Simply put, we followed his diagram and heated the solder connecting the pins to the ribbon, by heating *behind* the ribbon connector - applying the iron to the connections on the PCB, not the ribbon itself.



By starting at one end, and with a wide chisel-tip on the iron (wide enough to heat four pins at a time) we could reflow the solder and gently lift the ribbon clean off the PCB. It came of quickly and easily with no broken pins, traces or connectors. Brilliant!

We then laid the ribbon across our own PCB breakout board.... and found we'd made our traces too small. The ribbon was far too wide. Bugger. That meant we had to make up a new PCB (not actually a bad thing, since our earlier effort had a few broken traces anyway). An hour or so later and we'd managed to etch a couple of new breakout boards (its always worth having one spare, just in case). This time, the traces on the PCB and the connectors on the LCD ribbon lined up perfectly.

The ribbon was connected by using the same wide chisel-tip, and running the iron quickly across the top of the ribbon, flowing the solder onto the homemade PCB breakout board. Luckily there was enough solder left on the ribbon to connect pins to our PCB.



The last thing to do was connect the pin headers to the 2 x strips of 18-holes and see if we could get the thing to switch on. That's where we're up to now (it was a late one last night, getting the ribbon connected onto the PCB). Although we don't know the initialisation sequence, at this stage, it'd just be nice to see a backlight or something come on, if only to show we haven't made a mess of soldering the ribbon cable to the PCB board!

We've checked and double-checked all the pins for continuity, and checked that there are no shorts across the pins (they're tiny little traces in case you hadn't noticed!) so in theory it should work first time.........

0

320x240 QVGA LCDs

Posted by Chris on Tuesday, November 10, 2009
Here's a picture of a K838 digital photo frame, cracked open:


It basically consists of:
An AX203 microcontroller
A 2.4" LCD (320x240 pixels, QVGA, 65l colours)
Flash memory
Battery charging/protection circuit
USB plug/socket
A few crystals (one for driving the USB, one for the real-time-clock RTC)



We found similar LCD screens (the screen only, not in a nice enclosure) on eBay for about £12 each, so ordered a couple to have a play with - they may or may not use the same LCD driver chip; we'll soon find out! The LCDs on eBay also have an ingenious method of connecting the ribbon cable to the printed PCB - each exposed pin on the ribbon has a hole drilled through it. So when solder is applied to the top of the pin, a tiny dot can pass through, welding it to the PCB underneath.



Because we like working with big, fat, 0.1" breadboards and soldering to huge great big pads, we decided to have a go at building an LCD-to-DIP connector. Rather that a single line of pins, we built a double-inline-pin connector, because the LCD has no less than 36 pins to interface with. (Putting them into a two lines just seemed easier!). Bouyed by the brilliant comments on an earlier SMT project we figured that soldering to a tiny ribbon connector would be - if not easy - at least possible.



The etching on this one went a little awry. Actually, it's not as bad as it could be and the camera doesn't do the etching of the tiny fine traces justice. Although they appear blurred and merged together on the photo (highlighted on the left) each individual trace for the LCD ribbon is properly formed and complete. We should really have checked the traces leading back to the pins before etching, however, because - highlighted on the right - the connection to one of the pins has completed etched through. We'll have to patch this with a bit of silver paint or similar before testing...

0

Count Arthur Who?

Posted by Chris on Monday, November 09, 2009
"Oh, sod it? Which dozy idiot is ringing my bell now?"
If you're not familiar with the ramblings of Count Arthur Strong, you'd do well to tune into Radio4 every now and again for the Count Arthur Strong Radio Show. Yes, Radio4 - that'll sort the uber-nerds from the, erm, wheat-chaff thing. Although, if you're still in nerd-denial, you could always keep some dignity and tune in to Radio7 and catch some of the earlier shows when they are repeated.

Then again, you could always get some tickets and see the man himself performing live. If you're really lucky (as a few of us were last night) you might even get them for free (the BBC does not charge for tickets to see shows that are being recorded for television or radio).

If you like your comedy peppered with "malapropisms, spoonerisms, tall stories and archive footage meticulously stitched together… " as the Mail on Sunday would say (that's a quote lifted from the Count Arthur Strong website, not something that any of us would admit to reading) get yourself along to one of his live shows! Not only do you get the chance to hear yourself coughing/sneezing/farting from in the audience when the show is broadcast on the radio, but you also get to hear the outtakes, re-takes and general mucking about that goes on when things don't quite work out the first time around.
It's suitable for all the family, and you won't be embarrassed if you want to take along your 90-year-old Aunt Maud, nor do you need to worry about the kids picking up bad language either.

For a preview, check out the Radio4 website when no-one is looking.
If you're after BBC (i.e. free) tickets, get in there quick. The Count Arthur fan-base is growing quickly and already has a few new members after last night.

1

Reading PWM signals

Posted by Chris on Sunday, November 08, 2009
While waiting for the screens and samples for developing ScoreSure v2.0 I've been looking at a little project for Tom from RobotBrighton. He builds "battle robots" and has developed a relay-driven controller board for one of his 'bots (he has a habit of letting smoke out of his components and says he's burnt through quite a few MOSFETs already!).

The idea is to take RC (remote control) signals and convert them to on/off switching signals. RC signals work in a similar fashion to driving servo motors. A signal with a pulse width of x milliseconds is decoded at the other end to determine how far a joystick has been pushed on a remote control handset.

For this project, there are a few rules that have to be put into place as well as basic pwm-decoding: firstly, the signal width MUST be between 0.5ms-2ms. Any signal outside of this range should result in the controller sending an "error" signal, or putting the controlled device into a 5 second delay/standby mode.
Also, pulse widths for servo controls are often repeated at 2.5m/s intervals, although this may or may not be the case for the RC controller. So we need to measure the width of a signal coming in and act on it accordingly, but not necessarily worry about the frequency of the signals as these may be subject to fluctuation - i.e. we cannot assume that all signals start exactly 2.5ms apart.

To achieve this, we're going to use a humble 16F628A PIC chip.
It's cheap and available (but to be honest, for something relatively simple like this, it's a bit of overkill: a smaller/cheaper PIC could be used, but we've got quite a few of these hanging about, so we'll use one of these instead).
In order to accurately detect when a signal starts and ends, we're going to use the PICs interrupt routines. A PIC chip can generate interrupts in a number of scenarios. We're going to generate an interrupt on every change of input on pin RB4.



We also need to make use of a 16-bit timer and some pre-scaling, to calculate the width of each incoming signal pulse. Whenever pin RB4 goes high, an interrupt occurs and at this point the timer is reset to zero. We will also make use of the CCP module to generate an interrupt whenever the timer has counted the equivalent of 2 milliseconds. This will form the basis of our fail-safe.

So now, whenever an interrupt occurs we need to look at the state of the RB4 pin and the interrupt source to decide from which of the following three sources the interrupt came:

a) RB4 just went from low to high (we need to reset the timer)
b) RB4 just went from high to low (we need to read the timer to calculate the pulse width)
c) The CCP module is telling us that more than 2ms have elapsed since the start of the pulse (i.e. pwm value is outside the valid range)

By using these two interrupt sources, we do not need to worry about the frequency of the pulses, or about matching the timing of them. Whenever an invalid pulse width is read, the PIC goes into "standby" or error mode for a period of time, but when it comes out of that state, it can simply pick up on the next pulse that comes in - we don't have to worry about the PIC starting reading pulses from the wrong place in time (an effect referred to a phase-shift).

By following this approach, we should be able to read the value of the timer, whenever pin RB4 goes from high to low, and calculate the width of the pulse that generated the interrupt. If the pulse width is too short, we should put the PIC into a standby/error state. If the pulse width is too long, it will generate it's own error interrupt and will be handled accordingly. Any other value for the timer means we can see if it has passed a specific threshold value and toggle the on/off pin as necessary.

An alternative approach would be to poll the input pin(s) continuously, setting and resetting the internal timer as the pin state changed from high-to-low and back again. We may also explore this approach and compare the two.

0

Interfacing with Flash memory

Posted by Chris on Wednesday, November 04, 2009
Ok, here it is. Some Oshonsoft PIC Basic code for reading and writing data to a Flash memory device.
This example writes data through the Flash internal buffer(s) and reads/writes data one entire page (256 bytes) at a time. Because of the limitations of the USB implementation, the PIC keeps a mirror copy of the Flash buffer in memory and when called for, sends a group of 8 bytes to the USB host device.
This code example works with a PIC and Flash memory connected according to the schematic from this post, with the Flash memory connected to pins PORTA.0-3


Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x3e
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x05 '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

'SPI setup
Define SPI_CS_REG = PORTA
Define SPI_CS_BIT = 1
Define SPI_SCK_REG = PORTA
Define SPI_SCK_BIT = 2
Define SPI_SDI_REG = PORTA
Define SPI_SDI_BIT = 3
Define SPI_SDO_REG = PORTA
Define SPI_SDO_BIT = 0
SPIPrepare

'USB setup
UsbSetVendorId 0x099
UsbSetProductId 0x004
UsbSetVersionNumber 0x1001
UsbSetManufacturerString "ScoreSure"
UsbSetProductString "ScoreSure Template Device"
UsbSetSerialNumberString "0123456789"
UsbOnIoInGosub usbonioin
UsbOnIoOutGosub usbonioout
UsbOnFtInGosub usbonftin
UsbOnFtOutGosub usbonftout
AllDigital

'general declarations
Dim i As Byte
Dim j As Byte
Dim state As Byte
Dim iusb As Byte

'memory access variables
Dim addri As Byte
Dim addrj As Byte
Dim addrk As Byte
Dim ai As Byte
Dim bi As Byte
Dim ci As Byte
Dim useusb As Bit
Dim pageaddr As Word
Dim byteaddr As Byte
Dim bytecount As Word
Dim picbuffer(256) As Byte
Dim picbufferpointer As Byte
Dim flashstatus As Byte

'initialisation
init:
  useusb = 1
  picbufferpointer = 0

'startup
start:
  If useusb = 1 Then UsbStart


'main loop
loop:
  If useusb = 1 Then
    UsbService
  Else
    Select Case state

    EndSelect
  Endif

Goto loop
End

usbonftout:
  'feature reports.
Return

usbonftin:
  'don't do anything on feature.
Return

usbonioout:

  'received data from host into PIC
  'Here we put a single command byte into buffer(7) to tell the PIC
  'what we want it to do.

  Select Case UsbIoBuffer(7)

    '---------200+ are memory control commands --------

    '-----------------------------------------------------------
    Case 200 'set page address 0-2048 (byte 2= MSB, byte 1=LSB)
    '-----------------------------------------------------------
    iusb = UsbIoBuffer(2)
    pageaddr = iusb * 256
    iusb = UsbIoBuffer(1)
    pageaddr = pageaddr + iusb

    '-----------------------------------------------------------
    Case 201 'set byte address within a page (1 page=256 bytes)
    '-----------------------------------------------------------
    byteaddr = UsbIoBuffer(2)

    '---------------------------------------------------------
    Case 202 'load flash memory page into flash memory buffer
    '---------------------------------------------------------
    Gosub flashmemorytoflashbuffer

    '--------------------------------------------------------------
    Case 203 'load flash memory buffer (256 bytes) into PIC buffer
    '--------------------------------------------------------------
    Gosub flashbuffertopicbuffer

    '-------------------------------------------
    Case 204 'write PIC buffer to memory buffer
    '-------------------------------------------
    Gosub picbuffertoflashbuffer

    '-------------------------------------------
    Case 205 'flash buffer to flash page memory
    '-------------------------------------------
    Gosub flashbuffertoflashmemory

    '---------------------------------------
    Case 206 'set picbuffer pointer (0-255)
    '---------------------------------------
    picbufferpointer = UsbIoBuffer(6)

    '------------------------------------------------
    Case 207 'load 4 bytes from host into pic buffer
    '------------------------------------------------
    For iusb = 0 To 3
      picbuffer(picbufferpointer) = UsbIoBuffer(iusb)
      picbufferpointer = picbufferpointer + 1
    Next iusb

    '---------------------------
    Case 208 'start stream read
    '---------------------------
    streamread = 1
    SPICSOn
    SPISend 0xe8 'stream read (legacy mode)
    SPISend pageaddr.HB
    SPISend pageaddr.LB
    SPISend byteaddr
    SPISend 0x00 'four don't care bytes
    SPISend 0x00
    SPISend 0x00
    SPISend 0x00

    '-------------------------
    Case 209 'end stream read
    '-------------------------
    SPICSOff

    '--------------------------
    Case 210 'empty PIC buffer
    '--------------------------
    Gosub emptypicbuffer
    picbufferpointer = 0

    '-------------------------------------------------
    Case 240 'clear entire memory (use with caution!)
    '-------------------------------------------------

    '-------------------------------------------
    Case 253 'flash memory status register read
    '-------------------------------------------
    Gosub getflashstatus
    Gosub emptypicbuffer
    picbuffer(0) = flashstatus

    '-------------------------------------------------------------------
    Case 254 'blow the fuse on the flash memory to set to 256-byte mode
    '-------------------------------------------------------------------
    SPICSOn
    SPISend 0x3d
    SPISend 0x2a
    SPISend 0x80
    SPISend 0xa6
    SPICSOff

  EndSelect
Return

usbonioin:
  'send data from PIC to host
  'if you use a loop in here, garbage gets sent back!

  If streamread = 1 Then
    'load the data from flash memory (direct access, legacy mode)
    'straight into the usbiobuffer
    SPIReceive UsbIoBuffer(0)
    SPIReceive UsbIoBuffer(1)
    SPIReceive UsbIoBuffer(2)
    SPIReceive UsbIoBuffer(3)
    SPIReceive UsbIoBuffer(4)
    SPIReceive UsbIoBuffer(5)
    SPIReceive UsbIoBuffer(6)
    SPIReceive UsbIoBuffer(7)

  Else
    iusb = picbufferpointer
    UsbIoBuffer(0) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(1) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(2) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(3) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(4) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(5) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(6) = picbuffer(iusb)
    iusb = iusb + 1
    UsbIoBuffer(7) = picbuffer(iusb)

  Endif
Return

setflashdeepsleep:
  'when we're not using the flash memory, set it to deep sleep which causes it to
  'use minimum amount of power (it's basically on super-standby) opcode B9H
  SPICSOn
  SPISend 0xb9
  SPICSOff
Return

setflashwakeup:
  'when we've put the flash memory into deep sleep, it ignores all commands sent to
  'it until it's been woken up by sending opcode ABH
  SPICSOn
  SPISend 0x0ab
  SPICSOff
Return

flashmemorytoflashbuffer:
  SPICSOn
  SPISend 0x53 'transfer memory page to buffer1 command
  SPISend pageaddr.HB 'send the page address to read data
  SPISend pageaddr.LB 'from into buffer number one
  SPISend 0x00 'don't care 8-bits
  SPICSOff
Return

flashbuffertoflashmemory:
  SPICSOn
  SPISend 0x83 'transfer buffer1 to flash page (with built-in erase) command
  SPISend pageaddr.HB 'send the page address to read data
  SPISend pageaddr.LB 'from into buffer number one
  SPISend 0x00 'don't care 8-bits
  SPICSOff
Return

flashbuffertopicbuffer:
  SPICSOn
  SPISend 0xd4 'buffer1 read command
  SPISend 0x00 'start off with 16
  SPISend 0x00 'don't care bits
  SPISend 0x00 'then buffer byte to start reading from (always zero)

  For picbufferpointer = 0 To 255
    SPIReceive picbuffer(picbufferpointer)
  Next picbufferpointer
  picbufferpointer = 0
  SPICSOff
Return

picbuffertoflashbuffer:
  SPICSOn
  SPISend 0x84 'buffer1 write command
  SPISend 0x00 'start with 16
  SPISend 0x00 'don't care bits
  SPISend 0x00 'then buffer byte to start reading to (always zero)

  For picbufferpointer = 0 To 255
    SPISend picbuffer(picbufferpointer)
  Next picbufferpointer
  picbufferpointer = 0
  SPICSOff
Return

emptypicbuffer:
  For i = 0 To 255
    picbuffer(i) = 0
  Next i
Return

getflashstatus:
  SPICSOn
  SPISend 0xd7 'status register command
  SPIReceive flashstatus
  SPICSOff
Return


The first thing to do, once connected and the USB device recognised, is to set the page size to 256 bytes. This is done by sending the byte command (from PC to PIC)
number 254. Before doing this, we checked the Flash memory status register:
Send (from PC to PIC) byte command 253. This is done by clearing the entire USB buffer - setting all values to zero - on the PC then setting USB byte(7) to 253 using the VB command:
data(0)=0
data(1)=0
data(2)=0
data(3)=0
data(4)=0
data(5)=0
data(6)=0
data(7)=253
HIDTerminal1.HIDSendReport(data(0), data(1), data(2), data(3), data(4), data(5), data(6), data(7))

(see Oshonsoft PIC18 USB section for details about communicating with your PIC-based USB device from your PC and the HIDTerminal Activex control which encapsulates USB functionality.

After the byte command 253 has been sent to the USB device, the internal PIC memory buffer should be blank (all zeros) except for byte(0) which contains the Flash Status Register value. We read this back to the PC by issuing the VB command:
HIDTerminal1.HIDReadReport(data(0), data(1), data(2), data(3), data(4), data(5), data(6), data(7))

Displaying the values for data(0-7) on screen (we use a few text boxes, but you could use Debug.Print or any other method to view the values) we see that the Flash Status Register value is 156 or, in binary:
10011100

Checking the AT45DB041D datasheet we can see that the last bit of the status register (bit zero) tells us whether each page size is 264 (default) or 256.
In this case, we can see - because it's a brand new device - bit zero is cleared, which signifies that a page size is set to the default of size 264 bytes.
We issued the "change page size" command from PC to PIC (which in turn sent the byte sequence 0x3D, 0x2A, 0x80, 0xA6 to the Flash memory) to blow the fuse holding the page size value - effectively turning it to a 256-byte page size. This is a one-time operation and cannot be undone.
After repeating the "read Flash Status Register" routine as outlined above, we now got a status value of 157. This tells us that bit zero of the Flash Status Register is now set, which - according to the datasheet - signifies that the Flash unit is now working with a 256-byte page size. Just the result we were after!

Using a combination of send and read commands, we are now able to read and write data to and from the Flash memory device.
Result!

0

Nothing's ever easy!

Posted by Chris on Monday, November 02, 2009
Using the super-beefed-up-18F4550 chips for version 2.0 of our ScoreSure device and a whopping 4Mbit Flash memory chip has meant lots of ideas floating around Nerd Towers.

With a massive 320x240 qvga screen, and enough memory to hold the bitmap data, full-colour images are suddenly a possibility. Interfaces have been redesigned, with flashy graphics, unnecessary animations and more - think Homers Homepage and you'll get the idea!

Anyway, it all hinges on getting data into and out of the 4Mb flash memory chip. Which, according to the datasheet is relatively easy, since it supports "legacy mode" data access - i.e. provide a memory location (in eeprom this is a single number, in Flash eeprom it's split into "page number" and "byte number" but the principle is the same) then start streaming bytes back to the PIC via SPI. This method by-passes the buffers, so you can pretend they don't exist and treat it like a giant eeprom chip. Easy.

Writing data isn't quite as straight-forward.
For a start, there's no way of writing data directly to the main memory. We've read and re-read the datasheet and can't find of any way of bypassing the pesky buffers when writing data back to the chip. We scoured every page and came up with nothing.

Then someone pointed out the block diagram:


There it is, in easy-to-understand pictures!
Data from main memory straight to the i/o interface (serial lines). But nothing going the other way. All data has to be written to the buffers, then transferred into main memory. Which is a bit of a bugger as we've already written big chunks of code for moving data around, and it all assumed that you could interface directly with the main memory array. Once again, it looks like we're back to the drawing board....

whos.amung.us

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