C# And CFA635

Crypton

New member
Hi. I am developing an easier API to interface with the CFA635 LCD module. I got through the part of sending and receiving packets with the help of the neighbouring topic that deals with Visual Basic Express example.

I am having a problem of demystifying the art of bit mask reading. In the datasheet, for command to Read the Keypad in Polled Mode, it says that it returns a bit mask at data[0], but from that, I have absolutely no idea to determine which key was pressed. I never worked with bitmasks. I searched about it but came to no avail.

Made a small function which is overloaded (one is one-way, another returns the data)
Code:
byte[] response = SendPacket(command, dataLength, data, 3);
SendPacket() returns a byte array of response. So the 0th place would be type, 1st the data length, and so on.
Looking for additional LCD resources? Check out our LCD blog for the latest developments in LCD technology.
 
From the data sheet:

#define KP_UP 0x01
#define KP_ENTER 0x02
#define KP_CANCEL 0x04
#define KP_LEFT 0x08
#define KP_RIGHT 0x10
#define KP_DOWN 0x20

The return packet will be:
type = 0x40 | 0x18 = 0x58 = 8810
data_length = 3
data[0] = bit mask showing the keys currently pressed
... snipped ...

Each bit in data[0] represents a key pressed, so

if (data[0] & KP_ENTER) {}; // if true, enter key is pushed now
if (data[0] & KP_CANCEL) {}; // if true, cancel key is pushed now

etc., yada, yada.
 

Crypton

New member
I've tried what you suggested but it did not work. KeyPressed is an event handler that is supposed to be fired every time a key is pressed but when I debugged it, it was fired every single time in an odd sequence:

Code:
unsafe
            {
                if (Convert.ToBoolean(response[1]) & Convert.ToBoolean(0x04)) KeyPressed(Key.Cancel);
                if (Convert.ToBoolean(response[1]) & Convert.ToBoolean(Key.Down)) KeyPressed(Key.Down);
                if (Convert.ToBoolean(response[1]) & Convert.ToBoolean(Key.Enter)) KeyPressed(Key.Enter);
                if (Convert.ToBoolean(response[1]) & Convert.ToBoolean(Key.Left)) KeyPressed(Key.Left);
                if (Convert.ToBoolean(response[1]) & Convert.ToBoolean(Key.Right)) KeyPressed(Key.Right);
                if (Convert.ToBoolean(response[1]) & Convert.ToBoolean(Key.Up)) KeyPressed(Key.Up);
            }



Also, how do I write to the screen a CGRAM character? For example I want to write a 1/4 fraction (row 11d, col 128d on the map). Which command do I use? I tried 22 (0x16) and 0x1f but neither works.
 
Last edited:
I'm not familiar with C#, but I don't understand why you are using "Convert.ToBoolean()". To me, boolean means "true" or "false". So it seems like you're converting these values (from 1 to 32) to either "1" or "0", which effectively destroys the value (i.e. they are all "true"), so I can understand why its not working for you.

Why not use:
Code:
if (response[1] & Key.Down) KeyPressed(Key.Down);
//etc.
I'm just explaining the idea of "bit mapped"; I can't help you with other issues since I've never used this display.
 

Crypton

New member
If I don't, it throws a compiler error:
Error 1 Cannot implicitly convert type 'int' to 'bool' C:\Documents and Settings\...\Visual Studio 2008\Projects\CF635API\CF635API.cs 45 21 CF635API
 
Then maybe you need to do some type casting (is that part of C# language?). You don't show any variable declarations, so I am guessing.

if ((unsigned char)response[1] & (unsigned char)Key.Down) KeyPressed(Key.Down);
 

Heffo

New member
IIRC the If statement in C# only works with boolean results.. so you need to do something like the following..

Code:
if((response[1] & Key.Down) > 0)
{
    KeyPress(Key.Down);
}
That is assuming that "response" is a byte array and "Key" is a flag enum with it's type as a byte.

Code:
        [Flags]
        public enum Key : byte
        {
            Up = 0x01,
            Enter = 0x02,
            Cancel = 0x04,
            Left = 0x08,
            Right = 0x10,
            Down = 0x20
        }
 
Last edited:

Crypton

New member
Well after fiddling around the SerialPort class, I found that there is an event that is called each time when the Read buffer fills up, DataReceived.

Since the 0x80 Key Activity command will only give us 3 bytes, before we open the port, we can set port.ReceivedBytesThreshold = 3; (I don't know if I will want to move to command 30 (0x1E) to get more info from the LCD, but I will try it later).

So then I set up two events in my class,
Code:
public event KeyPressedHandler KeyPressed;
public event KeyReleasedHandler KeyReleased;
Which are called accordingly:
Code:
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] response = new byte[4];  /* Set the expected array */
            port.Read(response, 0, 4);      /* Read bytes from buffer */
            if (response[0] == 0x80)        /* 0x80 is called?        */
            {
                if (response[2] == (byte)KeyDown.Enter)
                   if(KeyPressed!=null) KeyPressed(KeyDown.Enter);
                if (response[2] == (byte)KeyDown.Down)
                    if (KeyPressed != null) KeyPressed(KeyDown.Down);
                if (response[2] == (byte)KeyDown.Exit)
                    if (KeyPressed != null) KeyPressed(KeyDown.Exit);
                if (response[2] == (byte)KeyDown.Left)
                    if (KeyPressed != null) KeyPressed(KeyDown.Left);
                if (response[2] == (byte)KeyDown.Right)
                    if (KeyPressed != null) KeyPressed(KeyDown.Right);
                if (response[2] == (byte)KeyDown.Up)
                    if (KeyPressed != null) KeyPressed(KeyDown.Up);

                if (response[2] == (byte)KeyUp.Down)
                    if (KeyReleased != null) KeyReleased(KeyUp.Down);
                if (response[2] == (byte)KeyUp.Enter)
                    if (KeyReleased != null) KeyReleased(KeyUp.Enter);
                if (response[2] == (byte)KeyUp.Exit)
                    if (KeyReleased != null) KeyReleased(KeyUp.Exit);
                if (response[2] == (byte)KeyUp.Left)
                    if (KeyReleased != null) KeyReleased(KeyUp.Left);
                if (response[2] == (byte)KeyUp.Right)
                    if (KeyReleased != null) KeyReleased(KeyUp.Right);
                if (response[2] == (byte)KeyUp.Up)
                    if (KeyReleased != null) KeyReleased(KeyUp.Up);
            }
            port.DiscardInBuffer();  /* Empty the buffer so new data can come in */
        }
 
Since the 0x80 Key Activity command will only give us 3 bytes, before we open the port, we can set port.ReceivedBytesThreshold = 3;
Since there are several report packets, of differing length, that may be received unpredictably, you should consider setting your port buffer threshold to 1, and handling the input on a byte-by-byte basis.

Suppose you send a command that expects a 5 byte response, and set your callback threshold to 5, and then before that response comes in, a report packet of 3 bytes comes in. Then you have a messy time with a partial packet in the buffer. Just food for thought.
 

Crypton

New member
Here comes another problem. :(

When executing command 1 (0x01) to get the firmware/hardware version, it returns a 'b' character but not the expected string:

Code:
public string GetVersion()
    {
            byte[] d = new byte[1];
            d[0] = 0;
            byte[] data = SendPacket(0x01, 0, d, 16);
            return Encoding.ASCII.GetString(data);
        }
I think I have everything right, but I am not really sure....

 

Ron

New member
cosmicvoid,

Can you please clarify what you mean by handling the input on a byte-by-byte basis?

I am working in C#, my code is very similar to the example code Crypton posted... this works fine as long as there is no other communication going on.

However, I am continually sending text to the LCD display - and intermittently, the button presses are not acknowledged.

I'm thinking that the other report packets coming in are causing the problem.

Many thanks,

Ron
 

Crypton

New member
It appears that there are packet collisions due to semaforing. Meaning that your instance has to always listening for packets and start a routing to examine the packet (for ex. start a thread), and immediately listen again. I think that this packet listener loop has to also run in a continuous loop (to check for packets & start the analyzer). I may be wrong though. :)

I built an API that can be used and the only thing left to create is the info from the LCD, at which I am stuck before releasing the lib.
 
... Can you please clarify what you mean by handling the input on a byte-by-byte basis? ... I'm thinking that the other report packets coming in are causing the problem.
The idea here is to always keep track of every packet that comes in, regardless of whether your app cares about the contents. This is the only way to be sure of where the packet boundaries are, because in some cases, the partial contents of a packet can look like the start of a new packet, and the only way you can tell it's not is that the CRC check will fail.

So by handling the input byte-by-byte, you are continuously parsing the data stream (as opposed to just looking for a certain command/response byte). Here is a sample of code that illustrates the packet parsing method that I use, look at post #23.

https://forum.crystalfontz.com/showthread.php?t=6276&page=2
 

Crypton

New member
Thanks for all the help! cosmicvoid I ported your C code over to C# and have the packet processor running on the separate thread and calling certain events accordingly.

Anyways, I may soon me releasing my API though it's just for 635 since that's all I have :(.
Meanwhile, I have some interesting ideas on GUI classes :)
 

Crypton

New member
Finally found some time to completely rewrite the API and make it more usable.
Anyways, here it is: http://crypton-technologies.net/lib/crystal-fontz-cf635-chsarp-api?lang=en

What does not work right now:
- all SCAB functions
- reporting function (0x1E)
- set LED color & intensity


I don't know when will I get a chance to finish SCAB functions as I do not own a SCAB module. But I will try to find some time to update the other functions.

The class is thread-safe so you can use it across many threads. But in best practices, always remember to lock your objects :)
 

rosswakelin

New member
Invalid key reads

Hi,

I found a copy of this DLL on the net, and am trying to use it to interface to a 635 using c#.
The DLL doesn't come with source code so I'm sort of in the dark here.
Is the author still on the forums?

Using the polled key press functions, it is returning the following wrong values:
UP returns UP
Down returns 32
Left returns 8
Right returns 16
Enter returns Down
Cancel returns right

The code is written to use enums, and it appears some of them are wrong, but without the source code I can't
fix it.
 

Crypton

New member
Hi,

I found a copy of this DLL on the net, and am trying to use it to interface to a 635 using c#.
The DLL doesn't come with source code so I'm sort of in the dark here.
Is the author still on the forums?

Using the polled key press functions, it is returning the following wrong values:
UP returns UP
Down returns 32
Left returns 8
Right returns 16
Enter returns Down
Cancel returns right

The code is written to use enums, and it appears some of them are wrong, but without the source code I can't
fix it.

I am still here more or less. Since then, I rewrote the library a little and I am releasing the current source code, but it is half way complete (as it, it may not be the best implementation, optimized, missing SCAB functions, etc). I welcome any criticism.

http://dl.dropbox.com/u/40766/635.zip
 

rosswakelin

New member
Many thanks, I'll give it a try tonight.
The old one worked fine for accessing the basic LCD functions, but I need to control the
LEDs and read the keys. The new code looks good for that too.

Thanks again.
 
Top