Archive

Author Archive

New CAN Main Board

I made a few adjustments to the board layout and also to the pcb-gcode setting and milled another circuit board. This one turned out nicely, even with a couple hiccups. First, the board wasn’t taped to the fixture exactly level, so a corner with nothing important didn’t mill through the copper (it was only the board outline). Then a phantom E-stop signal stopped the machine before it was done and had to restart from the beginning (I upped the debounce value to correct this). I thought it was another beverage coaster while watching it mill, but let it finish and it turned out fine. I ran the drill routine with a .035″ bit and everything but the screw terminal block and power jack fit. I was able to make the terminal block fit by sanding down the edges of the pins, but the power jack I’ll just use some wires and plug later.

Freshly milled CAN Main board, version 0.2.

Since the majority of components fit, I soldered this one up to give me an idea of what the next round of changes need to be. The main one is to increase the size of the pads to make it easier to solder and to increase the isolation path around them to prevent bridges to the ground plane. I’ll also have to change the through holes to the right size in the Eagle component library I made. At least the spacings were all right so everything still fit. Another change will be to the text ratio so the thin areas of copper don’t peel off when milled. They probably would have been fine if the second pass wasn’t done, but whatever.

Just needs a power connection and it’s ready to go.

Anyways, the CAN Main board only contains the basic circuit to run a 18F4580 micrcocontroller with the MCP2551 CAN transceiver; power from a 7805, 20MHz crystal oscillator, ICD plug, reset button, CAN status LED and then the rest of the pins are brought out to female headers to plug in a shield. I also included an area for the CAN bus termination resistor and three capacitors with jumpers, and a place to connect the cable shielding to the board’s ground. There is a jumper to bypass the 7805 regulator to use a 5V wall wart directly which are more common these days.

CNC Milling Circuit Boards

June 1, 2012 18 comments

I finally was able to mill a circuit board using my CNC machine. No more acid etching, but I never had much luck with it anyways. After milling the table and fixture, I was held up by the lack of 1/8″ collet, which Bosch doesn’t make. I bought a nice collet set from Think & Tinker/PreciseBits for a modest price and it seems it was worth it.  I also needed to buy a license for Mach3 to go beyond the 500 line code limit, since the code to etch the board was near 20,000 lines. To produce the G-code from an EagleCAD project, a nice script is available for free, http://www.pcbgcode.org/.  It installs like any other ULP script for EagleCAD and comes with docs to get you going, plus forums and plenty of other websites with tutorials.

First attempt and first scrapped board.

It was neat watching the CNC machine cut the traces and no broken bits or other catastrophes. Unfortunately, I didn’t take the time to properly zero the end of the 45 degree v-bit and cut too deep. This washed out the traces and pads, so it’s scrap. Before I realized this though, I tried fiddling with the settings for the pcb-gcode script and kept milling on the sheet of copper clad board and got a lot more bad traces.

This is only a test.

The correct way to zero the end of the tool was to use a piece of paper between the board and the tool and to jog down at a very low speed (about 3% of max). While the tool is going down, move the paper back and forth till the tip catches it. Now zero the Z-axis in the Offsets tab of Mach3 in one of special G-code offset codes of your choice. Then measure the thickness of the paper with calipers (my piece was .0035″) and subtract it from the Z offset (press the “Save Work Offset” button to bring up a dialog box that allows you to edit the coordinates). This puts the Z-axis 0 coordinate on top of the circuit board without any pressure. The X & Y coordinates are set to where you want to start milling the board, and can be done by eyeballing it, just don’t re-zero the Z-axis. Now the machine is set to mill.

Successfully milled board, for the smart CAN house project.

My settings for pcb-gcode script (not all are included, just what I changed):

Generation Options

Isolation

Default = .0001″

Maximum = .0001″

Step Size = .005″

Etching Tool Size = .007″

Machine

Z Axis

Z High = .5″

Z Up = .1″

Z Down = .002″ (someone posted an excel file in the pcb-gcode forums to calc this, I set mine .001″ higher and it worked)

Drill Depth = .065″ (go a little deeper than the board thickness)

Tool Change

X = 0

Y = 0

Z = .5″ (don’t leave as 0 or the machine will drag the end of the drill bit across the board between tool changes)

Feed Rates

XY = 10″/min

Z = 5″/min

Change any other options you need, then hit “Accept and make my board”. All the files generated will be placed in the project folder with the board. Check out the readme file in the docs folder for explanations, it was helpful. I’ll probably try setting the Maximum Isolation setting to something higher, so the slivers between close traces are removed. I’m sure I’ll update with more progress, or at least to show of stuff I made.

Troubleshooting a CAN Circuit

I’ll bet the term troubleshooting came from someone who had trouble, then started shooting. At least that’s what I would do if I still lived in an area where I could shoot something (damn neighbors). I’ve been trying for weeks to get the K-Node circuit to work with any of my other CAN capable boards, with no avail. First, I thought the bus had too much noise, and used the oscilloscope to find a extra 60 Hz wave causing mayhem. This was removed without success and then thought a shitty clock signal was to blame. It only slightly was inhibiting communication (it only caused a few errors, but not enough to totally block the message).

Anyways, turns out the CAN transceiver was screwed up beyond use. The MCP2551 chip is very sensitive to whatever, and fries when either of the bus wires are disturbed. I should have known, but didn’t. The previous post outlines my attempt at clearing up any extra noise on the bus. Then thought it could have been an inaccurate clock to the microcontroller. I bought some nice 20Mhz oscillators and replaced the crystal/caps on both the SpartaNode and K-Node boards, without success.

So before I threw both circuits, the scope, and all the other equipment I have out the window, I replaced the MCP2551 IC. Of course it worked right away. Guh, I suppose the lesson here is to make sure the CAN bus wires are secured very, very well and to absotively, not-arino hot swap the wires. I’ll pretend it wasn’t all for waste since the random, mysterious communication errors don’t show up during testing. Now that I can send a message between the K-Node and SpartaNode, I can run the CAN bus through my house and really test the system.

The Physical CAN bus

May 21, 2012 4 comments

I made some long overdue progress on my Smart House project that uses CAN bus. Up till now, the physical bus were two scrap wires I twisted together and soldered on a couple pins for plugging into breadboards. This wasn’t going to be an option once it was time to run the actual wires through my house, since it will be more than a few feet. A search for a single twisted pair, around 18 AWG and unsheathed yielded nothing, unfortunately. I settled on some security cable, which is two conductor 22 AWG, shielded and sheathed, as well as readily available for cheap at any hardware or big box home store.

Noisy noise on CAN bus.

For a test bus, I randomly cut a piece 12 feet long and soldered on 120 ohm resistors to each end, then connected to the K-node and SpartaNode. Of course it didn’t work, and probing with an oscilloscope showed a nice 60 Hz wave causing interference. The wave was present with or without power on and also near or far from other wires. The interference wave’s amplitude varied from about 1 volt peak to peak, to over 3V when moved around and was superimposed on any CAN messages on the bus. I suppose Murphy’s electrical law was in effect; the one about a wire being an antenna when you don’t want it to be.

How to get cap values.

The good news is the book, CAN System Engineering, held the solution. By using three capacitors per end, all the 60 Hz wave was removed and a crisp CAN message was able to travel just like in the test setup. The values of the capacitors are the same for both ends and depend on the length of the bus. For mine, I needed a 68pF cap between the CAN High and Low wires, and two 220pF caps from each to ground. I soldered these to the board (but just realized I shouldn’t have, d’oh) and added a place to connect the cable shield. The two nodes are still showing a communication error, so the same CAN message is being repeated for a response, but the at least you can see it’s nice and clean looking. I suspect it is a software issue.

 

 

 

Fun with Threshold Logic Gates

April 25, 2012 Leave a comment

Dammit, I tried writing the best darn paper I could in hopes of scoring a free trip to Vegas on university money, but costs a lot of money to have you paper submitted to the conference being held there. Oh well, I guess I ended up learning something, if that’s worth anything.

So the class project I did was to make a 4-bit full adder using threshold gates and written in Verilog HDL so it can be loaded onto an FPGA. A threshold gate is sort of a model of a neuron cell from the brain. Basically, the binary inputs (1 or 0) are multiplied by individual weights (positive or negative integers) and summed. Then the gate output is “1″ if the sum is greater than a threshold value, or “0″ is less. The benefit of this is the ability to reduce a complex Boolean function to one gate (certain restrictions apply). To find the weights and threshold value Dertouzos’ method is used, which surprisingly was not googleable.

Threshold Gate

Even if a function can’t be reduced to a single gate, multiple threshold gates can be connected together to form a neural network or cascaded to still be realized. The sum function for a full adder can’t be realized as one, but the carry out can be. Luckily another method, called IMS (Implied Minterm Structure), does the trick and groups the minterms so Dertouzos’ method works. Using IMS, three threshold gates are needed to cover the sum minterms and then connected together with a forth.

Threshold gates for Full Adder

Now that the number crunching is done, its time to code this into Verilog. Supposedly real numbers can be used, but the old version in the lab couldn’t, so I had to settle for integers. To get the code to compile, all of the threshold values needed to be rounded up and then the threshold inequality needs to be changed to greater or equal. Since each threshold gate has a different combo of weights and threshold, each one was written separately, then combined as a full adder module and finally into a 4-bit full adder. It even worked when loaded into an FPGA (Spartan variety). Below is the code so you can claim it as original for you’re next class project or perhaps for some nerdy shits and giggles.

module threshold1(f, A, B, C);
    output f;
    input A,B,C;
    reg f;

    parameter integer w1 = 1, w2 = 1, w3 = 1, t = 1;
    integer out;

    always@(A or B or C)
    begin
    out = A*w1 + B*w2 + C*w3;
        if (out >= t)
            f = 1'b1;
        else
            f = 1'b0;

    end
endmodule

module threshold2(f, A, B, C);
    output f;
    input A,B,C;
    reg f;

    parameter integer w1 = 1, w2 = 1, w3 = 1, t = 2;
    integer out;

    always@(A or B or C)
    begin
    out = A*w1 + B*w2 + C*w3;
        if (out >= t)
            f = 1'b1;
        else
            f = 1'b0;

    end
endmodule

module threshold3(f, A, B, C);
    output f;
    input A,B,C;
    reg f;

    parameter integer w1 = 1, w2 = 1, w3 = 1, t = 3;
    integer out;

    always@(A or B or C)
    begin
    out = A*w1 + B*w2 + C*w3;
        if (out >= t)
            f = 1'b1;
        else
            f = 1'b0;

    end
endmodule

module threshold4(f, A, B, C);
    output f;
    input A,B,C;
    reg f;

    parameter integer w1 = 1, w2 = -1, w3 = 1, t = 1;
    integer out;

    always@(A or B or C)
    begin
    out = A*w1 + B*w2 + C*w3;
        if (out >= t)
            f = 1'b1;
        else
            f = 1'b0;

    end
endmodule

module threshold5(f, A, B, C);
    output f;
    input A,B,C;
    reg f;

    parameter integer w1 = 1, w2 = 1, w3 = 1, t = 2;
    integer out;

    always@(A or B or C)
    begin
    out = A*w1 + B*w2 + C*w3;
        if (out >= t)
            f = 1'b1;
        else
            f = 1'b0;

    end
endmodule

module fulladder (sum, c_out, A, B, c_in);
    output sum, c_out;
    input A, B, c_in;
    wire x, y, z;

    threshold1 n1 (x, A, B, c_in);
    threshold2 n2 (y, A, B, c_in);
    threshold3 n3 (z, A, B, c_in);
    threshold4 n4 (sum, x, y, z);

    threshold5 n5 (c_out, A, B, c_in);

endmodule

module full_4bit_adder (sum, c_out, A, B, c_in);
    output [3:0] sum;
    output c_out;
    input [3:0] A;
    input [3:0] B;
    input c_in;
    wire x, y, z;

    fulladder a1 (sum[0], x, A[0], B[0], c_in);
    fulladder a2 (sum[1], y, A[1], B[1], x);
    fulladder a3 (sum[2], z, A[2], B[2], y);
    fulladder a4 (sum[3], c_out, A[3], B[3], z);

endmodule
Categories: FPGA Tags: , , ,

Fixture for PCB Routing

April 17, 2012 4 comments

I hope my neighbors don’t mind me milling things late at night. The vacuum is the loudest over the router and stepper motors, so they’ll probably just think I like my house really clean. To kick off my CNC adventures, I’m making a fixture to hold PCBs for isolation routing.

SketchUp model of fixture.

It’s nothing fancy, it mainly helped me understand the CAM portion making stuff. I ended up not using the Sketchup model because of added learning involved to use a script or something, but it can be done. Instead I used CAD to draw the square with the rounded out corner reliefs, then used the CAM software CamBam to setup the stock size and generate G-code.  My worst problem was trying to make the coordinate systems jive between the CamBam and my machine. In standard drawing coordinates, the +x goes to the right, while my machine’s +x moves left. This was corrected with an option to set a separate  machining origin, and also using offsets in the machines software.

The raw PCB fits nice!

I made the pocket for the PCB slightly larger than the width and length of the raw stock and to a depth of .060″ to match the thickness. The corners were over cut to allow the PCB’s square corners to fit. Next I’ll be drawing up some various clamps to go over the edges to press down. By using some double sided tape underneath, the PCB should be a little higher to give the clamps something to press into. I’m also going to add two locator pins so the fixture can be taken off and put back on in the same place. Built into G-code are places to store fixture locations and makes having multiple fixtures even easier. I milled the main pocket and corners separate and both programs aligned perfectly, Yay!

Categories: CNC Machine Tags: ,

CNC Machine Table Milled

April 15, 2012 1 comment

Yay, three times is the charm. My CNC machine finally milled it’s table flat without motors stalling or random nuts coming loose. Since the second attempt, I tweaked the bearing adjustments on the Y-axis and upped the current to all the motors for more torque. The motors got slightly hotter, but not so much that I couldn’t touch them. I also ran the table cutting program a few times without a tool to work out any other kinks.

Table finally milled.

Cutting the table seemed to take out the inaccuracies from building and you can see in the corners where the high sides were cut shallower than the low side. When I glued the table together I didn’t notice it was twisted slightly on one end. Luckily it could be compensated for in the end.

High corner cut .112" deep.

Lowest corner cut .275" deep.

The run wasn’t without its problems though. Being so pleased with the dust extractor I built, I got a new filter for the shop vac for maximum performance. It was too much for the all-purpose bucket, and the vacuum imploded it slightly and let all the wood dust through to the vacuum. Once the vacuum filter clogged it quit working and the dust went everywhere. Oh well, I bought a new bucket and some extra filtering material and it seems to be working again.

It held a vacuum, even while being sucked in further.

So now I can start making stuff on it, but since I’ve been putting my energy into building it, I’m not sure how to do that. I’ll probably start by making a fixture and hold-down clamps for circuit boards and plastic sheet. I’ve already read some webpages that I’ll try and emulate, so I just need to put it all together.

Categories: CNC Machine

Second Try & Abort

April 10, 2012 Leave a comment

I took a second shot at planing the table on my DIY CNC machine, but had to abort after several passes. All the new systems were working well, new router, dust extractor and vacuum system, but a rouge nut and washer appeared on the far side of the table. Once the machine was stopped with the E-stop button I found the nuts holding the bottom Z-axis drive shaft bearing mount were vibrating off.

Nut coming loose from under side of Z-axis carriage. Whoops-a-doodle.

I added some lock washers to all six bolts on the bottom side of the Z-axis carriage to prevent that from happening again. I suppose I should start checking things like nuts coming loose before firing up to cut, so lesson learned. It was lucky the nut and washer fell off out of the way of the router and that I noticed it.

Ready for the third attempt to mill the table flat.

Actually, I needed to restart from the beginning of the program with an offset down to correct a step between the first and second attempts. This also let me check if the new cyclone dust extractor worked on the fine saw dust produced from cutting the MDF board. The shroud made from a CD spindle cover worked rather well for its first real test. I’ll think about adding some brushes around the perimeter later, but was satisfied at how well it controlled the dust cloud that I’ll continue without for now.

Categories: CNC Machine Tags:

Cyclone Dust Extractor

April 10, 2012 8 comments

Before I start cutting again, I need a dust extractor to go along with the vacuum. The filter on the shop-vac was clogged with the dust I created from the first run so much it lost suction. Instead of spending $10 every few minutes on filters, I made a cyclonic dust extractor for the same amount. Since anyone with a CNC machine that cuts has one, there are plenty of plans online. I looked at a couple using a 5 gal. bucket and some PVC fittings that looked easy and went with that.

The $10 cyclone dust extractor.

The input to the extractor comes from the CNC machine and to a 90 degree elbow pointed toward the inside wall of the bucket. Then the output to the shop-vac is a single straight piece of PVC pipe, with the end inside the bucket higher than the elbow. Nothing complicated and parts were easily found at the big box store; 2″ PVC pipe section, elbow and two couplings and a general purpose bucket with lid. The hard part was trying to match  the two different hose diameters.

Dust filled air enters through the elbow, then falls due to gravity and letting the cleaner air continue to the shop-vac out the short pipe.

After gaining a third section of hose, sacrificed from a junk vacuum in the basement, I was able to make a permanent connection to the shroud and bungee cords. The shop-vac hose hooked to this new hose like it was meant to be, but was a trick to find a compatible PVC piece for the opposite end. I used my dremel tool and ground a step into the inner diameter of a section of 2″ PVC pipe to allow the shop-vac hose to fit into. I’ll still be able to disconnect this section to use the shop-vac for cleaning elsewhere. The last section of hose was an extension kit I bought for a different brand of shop-vacs and was naturally incompatible.  One end fits into the vacuum’s input well enough, but the opposite end to the extractor wasn’t near anything. I wound up cutting a slot on one end of a section of 2″ pipe and squeezed the end together with a pipe clamp till the hose fit over. Once the hose was on, the pipe clamp was released and any remaining holes taped over.

The PVC pieces for the output. The two pieces on the bottom are from one coupling cut in half. The smaller half on the right is then cut through perpendicularly to the end.

With the all the fittings made, they need to be connected through the bucket lid. The two holes were cut with a craft knife and care needs to be taken not to rip the material, because it’s easy to do. I scrapped a lid trying to epoxy the two PVC pipes in place, with both times breaking off immediately when moved side to side. Next I sandwiched the lid between two edges of pipe coupling that worked well. For the output section I cut a coupling in half, then cut through perpendicularly on only one of those halves (just one cut so it stays a ring, but opens wider). The solid coupling half goes on the straight pipe section, inside the bucket. On top of the lid, the cut coupling half can expand around the pipe to go all the way down easily. The PVC cement seems to melt the lid material to the pipe and fuses everything securely (and gives off some bonus fumes). The input side also needs a coupling cut in half, but only the piece with the extra cut like before; the edge of the elbow serves as the bottom part of the sandwich.

Only half of a coupling is needed for the input side, cut to go over the pipe easily.

Once the glue dried and the hoses were connected, I fired up the vacuum to test for leaks. Where hoses met, I sealed with duct tape. Then sealed leaks in the PVC/lid area with some silicone. While the vacuum was on, the wet silicone sucked into holes. I also revised the bungee cord rigging to hold the hose off of the work surface. With the gantry at is furthest position, the bungee cord was wrapped around the hose a few times and held in places with zip ties. When the gantry moved back to home, the hose naturally coils against the wall and out of the way. From the pic below, it looks like the cord is pulling hard on the hook in the wall, but there is still some slack left.

It worked! Some of the dust made it past to the shop vac, but not as much as this.

Eventually, I’ll move both the dust extractor and vacuum in the basement, with the switch controlled by the CNC software. But that will have to wait till I actually make something.

Categories: CNC Machine Tags:

Microchip Graphics Library Integration

March 23, 2012 10 comments

Here is some code for the eBay HX8347-A LCD I’ve been dinking with that utilizes the Graphics Library provided by Microchip. They have a few nice app notes on creating the graphical widgets on one of their development boards, but not really for a custom setup like my Commander32. All of their microcontrollers use the PMP feature to communicate with the LCD, while I just use pins directly.  Their help file gives a nice summary of copying the library files to a new project to keep the file structure intact. Once that is done, the following steps should help make your own setup.

Title Screen for GUI demo

Main Menu with images.

Button pressed on Main Menu.

  1. Write a new HardwareProfile.h

Write from scratch to contain the definition of the LCD pins to the microcontroller pins  and the clock frequency define for SYS_FREQ (required for library). This goes in source folder with your main.c file.

#define SYS_FREQ        (80000000)

// Definitions for reset pin
#define RST_TRIS_BIT    TRISGbits.TRISG12
#define RST_LAT_BIT     LATGbits.LATG12
#define    LCD_REST        RST_LAT_BIT

// Definitions for CS pin
#define CS_TRIS_BIT     TRISGbits.TRISG13
#define CS_LAT_BIT      LATGbits.LATG13
#define    LCD_CS            CS_LAT_BIT

// Definitions for RS pin
#define RS_TRIS_BIT        TRISGbits.TRISG15
#define RS_LAT_BIT      LATGbits.LATG15
#define    LCD_RS            RS_LAT_BIT

// Definitions for WR pin
#define WR_TRIS_BIT        TRISGbits.TRISG14
#define WR_LAT_BIT        LATGbits.LATG14
#define    LCD_WR            WR_LAT_BIT

// Definitions for WR pin
#define RD_TRIS_BIT        TRISDbits.TRISD9
#define RD_LAT_BIT        LATDbits.LATD9

// Data Port for 16 bits
#define    LCD_DATA        LATB
#define    LCD_DATA_TRIS    TRISB
#define DataPortIn()  LCD_DATA_TRIS = 0xffff;
#define DataPortOut() LCD_DATA_TRIS = 0x0000;
  1. Custom LCD Driver (HX8347A_16BIT.c &.h)

The Library contains some blank custom driver files (.c and .h in separate folders w/in library) with the two basic functions, DeviceReset() and PutPixel() required, but empty. My previous LCD initialization code was dumped into DeviceReset() and similarly with the PutPixel(). I also needed to write my own routines for ClearDevice() and Bar(), to optimize drawing. This required the appropriate lines in the header file to be uncommented to use these functions over the Library’s.

HX8347A_16BIT.c

/*****************************************************************************
 *  Module for Microchip Graphics Library
    HX8347 LCD Controller
 *  Custom display controller driver template
 *  By: Ryan Milewski, 2012
 *****************************************************************************/
#include "Graphics/Graphics.h"
#include "HardwareProfile.h"

// Color
WORD_VAL    _color;

// Clipping region control
SHORT       _clipRgn;

// Clipping region borders
SHORT       _clipLeft;
SHORT       _clipTop;
SHORT       _clipRight;
SHORT       _clipBottom;

/*********************************************************************
* Function:  void  DelayMs(WORD time)
* PreCondition: none
* Input: time - delay in ms
* Output: none
* Side Effects: none
* Overview: delays execution on time specified in ms
* Note: none
********************************************************************/
void DelayMs(unsigned int msec)
{
    unsigned int tWait, tStart;

    tWait=(SYS_FREQ/2000)*msec;
    tStart=ReadCoreTimer();
    while((ReadCoreTimer()-tStart)<tWait);        // wait for the time to pass

}

void LCD_write_COM(WORD data)
{

    LCD_RS = 0;
    LCD_CS = 0;
    LCD_DATA = data;
    LCD_WR = 0;
    //Nop();
    //Nop();
    //Nop();
    LCD_WR = 1;
    LCD_CS = 1;    

}

void LCD_write_DATA(WORD data)
{
    LCD_RS = 1;
    LCD_CS = 0;

    LCD_DATA = data;
    LCD_WR = 0;
    //Nop();
    //Nop();
    //Nop();
    LCD_WR = 1;
    LCD_CS = 1;

}//end of WriteLCD_DATA()

void LCD_write_COM_DATA(BYTE index, WORD value)
{
    LCD_write_COM(index);
    LCD_write_DATA(value);

}

/*********************************************************************
* Function:  void ResetDevice()
* PreCondition: none
* Input: none
* Output: none
* Side Effects: none
* Overview: resets LCD, initializes PMP
* Note: none
********************************************************************/
void ResetDevice(void)
{

    // Set uController pins TRIS
    AD1PCFG = 0xffff;    //analog pins all digital

    CS_TRIS_BIT = 0;
    RS_TRIS_BIT = 0;
    WR_TRIS_BIT = 0;
    RD_TRIS_BIT = 0;
    DataPortOut();

    //Reset LCD    
    LCD_REST = 1;
    DelayMs(5);    //5ms delay
    LCD_REST = 0;
    DelayMs(10);    //10ms delay
    LCD_REST = 1;
    DelayMs(20);    //20ms delay

    //Vendor gamma
    LCD_write_COM_DATA(0x0046,0x00a4);
    LCD_write_COM_DATA(0x0047,0x0053);
    LCD_write_COM_DATA(0x0048,0x0000);
    LCD_write_COM_DATA(0x0049,0x0044);
    LCD_write_COM_DATA(0x004a,0x0004);
    LCD_write_COM_DATA(0x004b,0x0067);
    LCD_write_COM_DATA(0x004c,0x0033);
    LCD_write_COM_DATA(0x004d,0x0077);
    LCD_write_COM_DATA(0x004e,0x0012);
    LCD_write_COM_DATA(0x004f,0x004c);
    LCD_write_COM_DATA(0x0050,0x0046);
    LCD_write_COM_DATA(0x0051,0x0044);

    //240x320 window settings
    LCD_write_COM_DATA(0x0002,0x0000);    //Column address start2
    LCD_write_COM_DATA(0x0003,0x0000);    //column address start1
    LCD_write_COM_DATA(0x0004,0x0000);    //Column address end2
    LCD_write_COM_DATA(0x0005,0x00ef);    //Column address end1
    LCD_write_COM_DATA(0x0006,0x0000);    //Row address start2
    LCD_write_COM_DATA(0x0007,0x0000);    //Row address start1
    LCD_write_COM_DATA(0x0008,0x0001);    //Row address end2
    LCD_write_COM_DATA(0x0009,0x003f);    //Row address end1

    //display settings
    LCD_write_COM_DATA(0x0001,0x0006);    //IDMON=0,INVON=1,NORON=1,PTLON=0
    LCD_write_COM_DATA(0x0016,0x0078);    //MY=0,MX=0,MV=0,ML=1,BGR=0,TEON=0
    LCD_write_COM_DATA(0x0023,0x0095);    //N_DC=1001 0101
    LCD_write_COM_DATA(0x0024,0x0095);    //PI_DC=1001 0101
    LCD_write_COM_DATA(0x0025,0x00ff);    //I_DC=1111 1111

    LCD_write_COM_DATA(0x0027,0x0002);    //N_BP=0000 0010
    LCD_write_COM_DATA(0x0028,0x0002);    //N_FP=0000 0010
    LCD_write_COM_DATA(0x0029,0x0002);    //PI_BP=0000 0010
    LCD_write_COM_DATA(0x002a,0x0002);    //PI_FP=0000 0010
    LCD_write_COM_DATA(0x002c,0x0002);    //I_BP=0000 0010
    LCD_write_COM_DATA(0x002d,0x0002);    //I_FP=0000 0010

    LCD_write_COM_DATA(0x003a,0x0001);    //N_RTN=0000,N_NW=001
    LCD_write_COM_DATA(0x003b,0x0000);    //P_RTN=0000,P_NW=001
    LCD_write_COM_DATA(0x003c,0x00f0);    //I_RTN=1111,I_NW=000
    LCD_write_COM_DATA(0x003d,0x0000);    //DIV=00
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x0035,0x0038);    //EOS=38h
    LCD_write_COM_DATA(0x0036,0x0078);    //EQP=78h
    LCD_write_COM_DATA(0x003e,0x0038);    //SON=38h
    LCD_write_COM_DATA(0x0040,0x000f);    //GDON=0fh
    LCD_write_COM_DATA(0x0041,0x00f0);    //GDOFF

    //Power Supply Settings
    LCD_write_COM_DATA(0x0019,0x0049);    //CADJ=0100,CUADJ=100,OSD_EN=1,60Hz
    LCD_write_COM_DATA(0x0093,0x000f);    //RADJ=1111, 100%
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x0020,0x0040);    //BT=0100
    LCD_write_COM_DATA(0x001d,0x0007);    //VC1=111
    LCD_write_COM_DATA(0x001e,0x0000);    //VC3=000
    LCD_write_COM_DATA(0x001f,0x0004);    //VRH=0011

    //VCOM Settings
    LCD_write_COM_DATA(0x0044,0x004d);    //VCM=101 0000
    LCD_write_COM_DATA(0x0045,0x000e);    //VDV=1 0001
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x001c,0x0004);    //AP=100
    DelayMs(2);    //2ms delay
    LCD_write_COM_DATA(0x001b,0x0018);    //GASENB=0,PON=1,DK=1,XDK=0,VLCD TRI=0,STB=0
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x001b,0x0010);    //GASENB=0,PON=1,DK=0,XDK=0,VLCD TRI=0,STB=0
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x0043,0x0080);    //set VCOMG=1
    DelayMs(2);    //2ms delay

    //Display On Setting
    LCD_write_COM_DATA(0x0090,0x007f);    //SAP=0111 1111
    LCD_write_COM_DATA(0x0026,0x0004);    //GON=0,DTE=0,D=01
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x0026,0x0024);    //GON=1,DTE=0,D=01
    LCD_write_COM_DATA(0x0026,0x002c);    //GON=1,DTE=0,D=11
    DelayMs(1);    //1ms delay
    LCD_write_COM_DATA(0x0026,0x003c);    //GON=1,DTE=1,D=11

    //Internal Register Settings
    LCD_write_COM_DATA(0x0057,0x0002);    //TEST_Mode=1: into test mode
    LCD_write_COM_DATA(0x0095,0x0001);    //SET DISPLAY CLOCK AND PUMPKING CLOCK TO SYCRONIZE
    LCD_write_COM_DATA(0x0057,0x0000);    //TEST Mode=0:exit TEST Mode

    LCD_write_COM(0x0022);
}

void LCD_address_set(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
{
    LCD_write_COM_DATA(0x0002,x1>>8);        //Column address start2
    LCD_write_COM_DATA(0x0003,x1);    //Column address start1
    LCD_write_COM_DATA(0x0004,x2>>8);        //column address end2
    LCD_write_COM_DATA(0x0005,x2);    //column address end1

    LCD_write_COM_DATA(0x0006,y1>>8);        //Row address start2
    LCD_write_COM_DATA(0x0007,y1);    //row address start1
    LCD_write_COM_DATA(0x0008,y2>>8);        //row address end2
    LCD_write_COM_DATA(0x0009,y2);    //row address end1

    LCD_write_COM(0x0022);
}

/*********************************************************************
* Function: void PutPixel(SHORT x, SHORT y)
* PreCondition: none
* Input: x,y - pixel coordinates
* Output: none
* Side Effects: none
* Overview: puts pixel
* Note: none
********************************************************************/
void PutPixel(SHORT x, SHORT y)
{
    unsigned int ncolor;
    ncolor = GetColor();
    LCD_address_set(x,y,x,y);
    LCD_write_DATA(ncolor);
}

/*********************************************************************
* Function: WORD GetPixel(SHORT x, SHORT y)
* PreCondition: none
* Input: x,y - pixel coordinates
* Output: pixel color
* Side Effects: none
* Overview: returns pixel color at x,y position
* Note: none
********************************************************************/
WORD GetPixel(SHORT x, SHORT y)
{
    return (0);
}

/*********************************************************************
* Function: void ClearDevice(void)
* PreCondition: none
* Input: none
* Output: none
* Side Effects: none
* Overview: clears screen with current color and sets cursor to 0,0
* Note: none
********************************************************************/
void ClearDevice(void)
{
    int i,j;
    unsigned int ncolor;

    ncolor = GetColor();
    LCD_address_set(0,0,DISP_HOR_RESOLUTION-1,DISP_VER_RESOLUTION-1);

    for(i=0;i<DISP_HOR_RESOLUTION;i++)
    {
        for(j=0;j<DISP_VER_RESOLUTION;j++)
        {
        LCD_write_DATA(ncolor);
        }
    }
}

/*********************************************************************
* Function: WORD Bar(SHORT left, SHORT top, SHORT right, SHORT bottom)
* PreCondition: none
* Input: left,top - top left corner coordinates,
*        right,bottom - bottom right corner coordinates
* Output: For NON-Blocking configuration:
*         - Returns 0 when device is busy and the shape is not yet completely drawn.
*         - Returns 1 when the shape is completely drawn.
*         For Blocking configuration:
*         - Always return 1.
* Side Effects: none
* Overview: draws rectangle filled with current color
* Note: none
********************************************************************/
WORD Bar(SHORT left, SHORT top, SHORT right, SHORT bottom)
{
    unsigned ncolor;
    int i,j;

    ncolor = GetColor();

    LCD_address_set(left,top,right,bottom);
    for(i=0;i<=(right-left);i++)
    {
        for(j=0;j<=(bottom-top);j++)
        {
            LCD_write_DATA(ncolor);
        }

    }
    return (1);
}
  1. Select options in GraphicsConfig.h

This should be copied from Microchip and placed in your project folder (like the help file says). Put in the Horizontal and Verticle resolution defines your LCD and uncomment the Graphics options to be used.

  1. Replace DeviceDriver.h with Custom Driver

There are two Library files that need to be changed to use the custom driver in the #includes; Primitive.c and Graphics.h. In my case, #include “DeviceDriver.h” was replaced with “HX8347A_16BIT.h”.

Primitive.c 

#include "HardwareProfile.h"              // needed to provide values for GetMaxX() and GetMaxY() macros
#include "HX8347A_16BIT.h"        //Change to custom driver header HERE!!
#include "Graphics/Primitive.h"
#include "Compiler.h" 

 

Then,

Graphics.h

////////////////////////////// INCLUDES //////////////////////////////
    #include <stdlib.h>             // needed because of malloc()
    #include "GenericTypeDefs.h"
	#include "GraphicsConfig.h"
    #include "HX8347A_16BIT.h"      // Display Driver layer
    #include "Primitive.h"          // Graphic Primitives layer
    #include "GOL.h"                // Graphics Object layer

At this point, the Library’s Primitive Layer can be tested. I just grabbed some example code to draw the basic shapes available, text, and some images. Yay!

  1. Custom Touchscreen Driver

The files Touchscreen.c and.h were copied to my project folder and renamed ADS7843Touchscreen.c and .h for customizing. Microchip’s dev boards all drive the touch screen using the ADC, but mine uses the ADS7843 chip which takes care of all that. I kept the function TouchGetMsg() which processes the touch actions into predefined states, and added my own code for initializing and TouchGetXY(). My function gets both x and y coordinates at the same time and needed multiple readings of each and averaged to reduce noise. The ADS7843 has a convientent PenIRQ pin to signal an actual touch or an Idle condition (x = -1,y = -1).

ADS7843Touchscreen.c

SHORT touchX,touchY;

void spistart(void)
{
    D_CS = 1;
    D_CLK = 1;
    D_DIN = 1;
}//end of spistart()

void ADS7843_write(unsigned char num)
{
    unsigned int count,mask,temp;
    int i;
    
    D_CLK = 0;
    mask = 0x80;

    for(count=0;count<8;count++)
    {
        temp = mask&num;
        if (temp == 0)    D_DIN = 0;
        else    D_DIN = 1;
        
        //D_DIN = temp>>(count);
        D_CLK = 0;
        
        for(i=0;i<5;i++)
        {
            Nop();
        }
  
        D_CLK = 1;
        for(i=0;i<5;i++)
        {
            Nop();
        }
        mask>>=1;
    }
}//end of ADS7843_write()


unsigned int ADS7843_read(void)
{
    unsigned int count;
    unsigned int num;
    int i;
    
    num = 0;
    for(count=0;count<12;count++)
    {    
        num<<=1;
        D_CLK = 1;
        for(i=0;i<5;i++)
        {
            Nop();
        }

        D_CLK = 0;
        for(i=0;i<5;i++)
        {
            Nop();
        }

        if(D_DOUT) num++;
     }
    return(num);
}//end of ADS7843_read()



void TouchInit(void)
{
    //Port Init
    D_CLK_TRIS = 0;
    D_CS_TRIS = 0;
    D_DIN_TRIS = 0;
    D_DOUT_TRIS = 1;
    D_IRQ_TRIS = 1;

    spistart();

}

void TouchGetXY(void)
{
    int i,count,x_set,y_set;
    int TP_X,TP_Y;    

    count =0;
    x_set = 0;
    y_set = 0;

    while(count<8)
    {
        //PORTAbits.RA1 = 1;
        //Delay1KTCYx(4);    //2ms delay (8MHz clk)

        D_CS = 0;
        ADS7843_write(0xd4);

        D_CLK = 1;
        for(i=0;i<5;i++)
        {
            Nop();
        }

        D_CLK = 0;
        for(i=0;i<5;i++)
        {
            Nop();
        }

        y_set = y_set + ADS7843_read();
        count++;
    }
    
    count = 0;
    while(count<16)
    {

        ADS7843_write(0x94);
    
        D_CLK = 1;
        for(i=0;i<5;i++)
        {
            Nop();
        }

        D_CLK = 0;
        for(i=0;i<5;i++)
        {
            Nop();
        }

        x_set = x_set + ADS7843_read();

        
        count++;
    }
    D_CS = 1;
    TP_X = x_set/16;
    TP_Y = y_set/8;

    touchX = .0925*TP_X-.0040*TP_Y-31.1315;    //X-coordinate
    touchY = .0006*TP_X+.0661*TP_Y-19.5759;        //Y-coordinate
}

SHORT TouchGetX(void)
{
    int x;
    x = touchX;
    return(x);
}

SHORT TouchGetY(void)
{
    int y;
    y = touchY;
    return(y);
}

int TouchIRQ(void)
{
    int temp;
    temp = D_IRQ;
    return(temp);
}


Those functions will interact with Microchip’s TouchGetMsg(), and only needs ads7843touchscreen.h included in the main .c file source.

  1. Add required main() code

A little bit of code needs to be added to the main() while loop, which is shown more than once in a Microchip app note. Also a data structure, GOL_MSG msg, to relay events needs to be cast.

So hopefully your project is going. Adding and interacting with the widgets is pretty straight forward and documented in the help file (you should have been there already). I had several issues along the way, such as:

  • The default font. Solution: use Font25 or Font35 and cast correctly.
  • Start with the newest version 3 something, drastic changes were made and things didn’t work.
  • While developing touch screen driver, the data wasn’t being transferred to the rest of the GOL, so the widgets wouldn’t draw as pushed. Solution: start new project completely with same drivers. ?????
Follow

Get every new post delivered to your Inbox.