Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

LCD Help needed, 16x2s are boring


  • Please log in to reply
100 replies to this topic

#41 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 04 January 2011 - 11:51 PM

Omar, take another look at the second example on the Arduino site. They have an example of writing to random X,Y locations.

#42 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 04 January 2011 - 11:52 PM

I added a new feature on the WriteText method

        public void WriteText(string text, bool noSpace = true)
        {
            DataMode = true;

            if (noSpace)
                foreach (char c in text.ToCharArray())
                    _dataPort.Write(NokiaCharacters.ASCII[c - 0x20]);
            else
            {
                byte[] letter = new byte[6];
                foreach (char c in text.ToCharArray())
                {
                    for (int i = 0; i < 5; i++)
                        letter[i] = NokiaCharacters.ASCII[c - 0x20][i];

                    letter[5] = 0x00;
                    _dataPort.Write(letter);
                }

            }
        }
This will give you the option of adding a little one pixel line between letters, it makes them easier to read but less characters will fit on one line.

#43 Basiclife

Basiclife

    Member

  • Members
  • PipPip
  • 27 posts

Posted 05 January 2011 - 12:11 AM

Ok, so I just ordered one lol - I have no doubt that I'll be able to get it to work (by standing on the shoulders of giants). Please let us know if you find any more "gotchas" :)

#44 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 05 January 2011 - 12:46 AM

Omar, take another look at the second example on the Arduino site. They have an example of writing to random X,Y locations.


I cant't find it, could you get me a link or point me in the right direction? Also will you be posting a Projects showcase thing for this LCD? I think you should since you did most, if not all, of the work.

#45 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 05 January 2011 - 08:56 AM

This is what you need:

void gotoXY(int x, int y)
{
  LcdWrite( 0, 0x80 | x);  // column
  LcdWrite( 0, 0x40 | y);  // row
}

This is from http://www.arduino.c...nd/Code/PCD8544

Look for the text that reads:

A simple modified example of interfacing with the Nokia 3310 LCD that will print characters at an XY position on LCD and also will draw lines on LCD.

Writing individual pixels will be tricky (but certainly 'do-able') because the display does not have a plain vanilla X,Y bitmap. The datasheet goes into more detail on the arrangement of the bitmap; it seems to be optimized for 6 lines of characters. You might be better off creating a 84x48 array (i.e. a backbuffer) in the Netduino memory, writing pixels to the backbuffer, transforming it as appropriate for the LCD display and then copying the entire backbuffer to the display. That is what I am trying to do. It's only 504 bytes for the entire display, so it should be quick enough for animation.

#46 sweetlilmre

sweetlilmre

    Advanced Member

  • Members
  • PipPipPip
  • 62 posts

Posted 06 January 2011 - 02:07 PM

Hi, Its awesome you got this going :) I might have to get one now that you've done all the hard work :) -(e)

#47 bill.french

bill.french

    Advanced Member

  • Members
  • PipPipPip
  • 260 posts
  • LocationPrinceton, NJ

Posted 06 January 2011 - 02:44 PM

I might have to get one now that you've done all the hard work :)

LOL I ordered mine yesterday! B)

#48 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 07 January 2011 - 07:13 AM

I hacked the attached code together and (against my better judgement) post this without cleaning it up for others to see.

The routines in this code will allow you to write to arbitary pixels on the Nokia 84x48 screen. It is SLOW, but it works and should be a reasonable proof of concept for others to improve upon. The code is ugly and unorthodox, but the bmapSet(), bmapLine() and bmapWrite() methods should be useful to someone trying to figure out how to write to this little Nokia cellphone screen.

Believe it or not, this screen is similar in layout to the Commodore64 bitmap screen so it is tricky (but do-able) to draw things like diagonal lines because you have to build the line bit by bit.

The loop near the bottom of the program draws a single pixel that will move diagonally around the screen. The movement is quite slow since my code redraws the entire screen each time. The performance of this can definitely be improved.

I hope this is useful; I had a bit of fun writing it.

EDIT: I just realized that I used some method calls to a Netduino library that I am writing, so things like 'backlight.Brightness = 50;' will not work for you. I'll remove that stuff and post a cleaner (and faster) version tonight.

Attached Files



#49 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 07 January 2011 - 08:57 PM

I hacked the attached code together and (against my better judgement) post this without cleaning it up for others to see.


I was actually finishing up the code I was working on. I decided not to make a draw pixel based on x and y. I decided to allow the use to store an array of bytes, custom characters that can be sent later. I'll post the code below.

#50 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 07 January 2011 - 09:03 PM

Go further down for newest code!

#51 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 08 January 2011 - 02:58 AM

I made some updates. 1. No dependencies on any external libraries. 2. Includes a circle drawing method -> bmapCircle(Point p, short radius); 3. Pixel copying to the device can be 6x faster if you give it a hint what line you are updating. 4. Replaced loops with lookup tables. Key methods to look at: bmapSet(Point p, bool onOff) bmapLine(Point from, POint to) bmapCircle(Point p, short radius) bmapClear() bmapWrite() bmapWriteF(short y) And you have already seen: writeText(string s) gotoXY(Point p) I hope someone finds this useful (and fun!) when your displays arrive! EDIT: I uploaded the wrong file...FIXED!

Attached Files



#52 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 08 January 2011 - 04:13 AM

I am almost done with my bitmap drawing program. I have a c sharp program in which you make the image and then send it via serial to the netduino and the netduino will then draw it. I might be posting it next weekend though because I have midterms coming up next week so I am going to be pretty busy studying. Alfred we should really try to figure out a way to put our code together and come up with a well balanced and useful library for this thing, we kinda went off in different directions. let me know if you'd like to do that.

#53 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 08 January 2011 - 01:50 PM

I am almost done with my bitmap drawing program. I have a c sharp program in which you make the image and then send it via serial to the netduino and the netduino will then draw it. I might be posting it next weekend though because I have midterms coming up next week so I am going to be pretty busy studying.

Alfred we should really try to figure out a way to put our code together and come up with a well balanced and useful library for this thing, we kinda went off in different directions. let me know if you'd like to do that.


Sure, I'd love to. I think that our efforts are complimentary, but we have very different coding styles.

#54 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 08 January 2011 - 10:50 PM

Sure, I'd love to. I think that our efforts are complimentary, but we have very different coding styles.

I looked at your code... I'm scared of it... I have no clue what is going on in there.... its so different than what I'm used to! Its alright though, I guess we can just offer people different styles and they can choose one. Not saying you code isnt nice, I'm just lost, I've never really seen that style before.

I am actually redoing my code again, i decided that since its best to send the bytes all at once I'll do it Windows form style, with a refresh method... idk, I think it might work out best, I actually think that you went in that direction too right? thats why you got the bool[48*84] ? i think... so yeah I'll do that too and see how it works out.

#55 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 09 January 2011 - 03:19 AM

Omar, did you run my code? It's fully self contained and it won't damage your Netduino or your LCD, I promise. (You might find the resulting pattern on the LCD 'kinda cute.) You likely be interested in bmapWrite(). It does what I imagine you want to do i.e. take a 84x48 bitmap and display it on the LCD. It does the transformation from an 84x48 array of bool into a 504 array of byte array for writing to the LCD via SPI.

#56 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 09 January 2011 - 03:39 AM

Omar, did you run my code? It's fully self contained and it won't damage your Netduino or your LCD, I promise. (You might find the resulting pattern on the LCD 'kinda cute.)

You likely be interested in bmapWrite(). It does what I imagine you want to do i.e. take a 84x48 bitmap and display it on the LCD. It does the transformation from an 84x48 array of bool into a 504 array of byte array for writing to the LCD via SPI.


Oh I'm sorry, I didn't mean I was scared of what it does, I meant scared to go through it and try to understand it. I know you would never harm someone's netduino. I'll test it soon.

I did that bool system, just finished it actually. Mine uses a bool[48][] instead of bool[4032]. I am working on: public void DrawRectangle(int x, int y, int width, int height, bool filled) now. I am loving this screen. I was used to computers and their huge memory and processing power, with the Netduino I've learned to make things as efficient as I can. The netduino is great, obviously when compared to a computer... yeah.

#57 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 09 January 2011 - 04:24 AM

Here is the latest update:

IMPORTANT: Please note that the pins have changed!


public class Nokia_5110
    {
        private bool[] _offRow = new bool[84];

        private readonly byte[] _pow = new byte[] { 1, 2, 4, 8, 16, 32, 64, 128 };

        private OutputPort _resetPort, _daComPin;
        private PWM _backlightPort;
        private SPI _dataPort;

        public bool[][] _pixelMap = new bool[48][];
        private byte[] _byteMap = new byte[504];

        private uint _backlight = 0;
        private bool _invd = false, _autoRefresh = true;

        public Nokia_5110(Cpu.Pin chipSelect = Pins.GPIO_PIN_D3, Cpu.Pin backlight = Pins.GPIO_PIN_D9,
            Cpu.Pin reset = Pins.GPIO_PIN_D7, Cpu.Pin dataCommand = Pins.GPIO_PIN_D8)
        {
            SPI.Configuration spiConfiguration = new SPI.Configuration(
                chipSelect,            // chip select port
                false,                 // IC is accessed when chip select is low
                0,                     // setup time 1 ms
                0,                     // hold chip select 1 ms after transfer
                false,                 // clock line is low if device is not selected
                true,                  // data is sampled at leading edge of clock
                2000,                 // clockrate is 15 MHz
                SPI.SPI_module.SPI1   // use first SPI bus
                );

            _dataPort = new SPI(spiConfiguration);
            _backlightPort = new PWM(backlight);
            _resetPort = new OutputPort(reset, true);
            _daComPin = new OutputPort(dataCommand, true);

            Initialize();
            Clear();
        }

        private void Initialize()
        {
            _resetPort.Write(false);
            _resetPort.Write(true);
            DataMode = false;
            _dataPort.Write(new byte[] { 0x21, 0xBF, 0x04, 0x14, 0x0C, 0x20, 0x0C });
            DataMode = true;
            Clear();
        }

        public void SetCursor(float x, float y)
        {
            DataMode = false;
            _dataPort.Write(new byte[] { (byte)(0x80 | (int)(x * 4)), (byte)(0x40 | (int)(y)) });
            DataMode = true;
        }

        public void WriteText(string text, bool noSpace = true)
        {
            DataMode = true;

            if (noSpace)
                foreach (char c in text.ToCharArray())
                    _dataPort.Write(NokiaCharacters.ASCII[c - 0x20]);
            else
            {
                byte[] letter = new byte[6];
                foreach (char c in text.ToCharArray())
                {
                    for (int i = 0; i < 5; i++)
                        letter[i] = NokiaCharacters.ASCII[c - 0x20][i];

                    letter[5] = 0x00;
                    _dataPort.Write(letter);
                }

            }
        }

        public void DrawRectangle(int x, int y, int width, int height, bool filled)
        {
            // thats what lines are for! oh right... I need to make a Draw line method... 
            if (width <= 1 || height <= 1) 
                return;

            int endX = x + width, endY = y + height - 2;

            bool[] line = new bool[84];

            for (int p = x; p < endX; p++)
                line[p] = true;

            _pixelMap[y] = line;
            _pixelMap[endY] = line;

            if (!filled)
            {
                line = new bool[84];
                line[x] = true;
                line[endX - 1] = true;
            }

            for (int h = y + 1; h < endY; h++)
                    _pixelMap[h] = line;

            if (AutoRefresh)
                Refresh();
        }

        public void Refresh()
        {
            int byteID = 0;

            for (int y = 0; y < 6; y++)
            {
                for (int x = 0; x < 84; x++)
                {
                        _byteMap[byteID] = ConvertToByte(_pixelMap[y * 8][x], _pixelMap[y * 8 + 1][x], 
                                           _pixelMap[y * 8 + 2][x], _pixelMap[y * 8 + 3][x], _pixelMap[y * 8 + 4][x], 
                                           _pixelMap[y * 8 + 5][x], _pixelMap[y * 8 + 6][x], _pixelMap[y * 8 + 7][x]);
                        byteID++;
                }
            }

            _dataPort.Write(_byteMap);
            SetCursor(0, 0);
        }

        private byte ConvertToByte(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7)
        {
            byte result = 0;

            result += (byte)((b7) ? _pow[7] : 0);
            result += (byte)((b6) ? _pow[6] : 0);
            result += (byte)((b5) ? _pow[5] : 0);
            result += (byte)((b4) ? _pow[4] : 0);
            result += (byte)((b3) ? _pow[3] : 0);
            result += (byte)((b2) ? _pow[2] : 0);
            result += (byte)((b1) ? _pow[1] : 0);
            result += (byte)((b0) ? _pow[0] : 0);

            return result;
        }

        public void Clear()
        {
            for (int r = 0; r < 48; r++)
                _pixelMap[r] = _offRow;
            
            if(_autoRefresh)
                Refresh();
        }

        private void Inverse(bool inverse)
        {
            _invd = inverse;
            DataMode = false;
            _dataPort.Write(inverse ? new byte[] { 0x0D } : new byte[] { 0x0C });
            DataMode = true;
        }

        public bool InverseColors
        {
            get { return _invd; }
            set { Inverse(value); }
        }

        private bool DataMode
        {
            get { return _daComPin.Read(); }
            set { _daComPin.Write(value); }
        }

        public uint BacklightBrightness
        {
            get
            { return _backlight; }
            set
            {
                if (value > 100)
                    value = 100;

                _backlightPort.SetDutyCycle(value);
                _backlight = 100;
            }
        }

        public bool AutoRefresh
        {
            get { return _autoRefresh; }
            set { _autoRefresh = value; }
        }
    }

Random Filled squares example:
            Random r = new Random();

            lcd.BacklightBrightness = 80;

            while (true)
            {
                int width = r.Next(84);
                int height = r.Next(48);

                lcd.DrawRectangle(r.Next(84 - width), r.Next(48 - height), width, height, true);
                Thread.Sleep(500);
                lcd.Clear();
            }


#58 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 09 January 2011 - 04:34 AM

Alfred I tried your code, looking great! I really like the animation, I'll have to figure out how it all works, its smaller than my class ! Although when I made mine I decided to trade space for speed, seeing as its an LCD I prefer the fast refresh rate more than the smaller class... still I can learn a lot from your code.... what is ^= ? I've never seen that. what does it do exactly...?

#59 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 09 January 2011 - 05:18 AM

Okay, I moved the bitmap code into its own class (BMap), added a Rect() procedure and cleaned it up a bit and removed any code that was not relevant to the task at hand.

Posted Image

And for fun, I drew a cute picture!

I hope you enjoy!

Attached Files



#60 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 09 January 2011 - 06:34 AM

I decided to start from scratch yet again, I am not happy with my current memory and cpu usage. I am trying to get this perfect so people that just got this screen can use it easily and as efficiently as possible.

Alfred I decided to use your genius struct system, thanks for that code. I added the Rectangle struct. The code below will be changing as I make things work and finish methods... This will be some of my cleanest, most efficient code. I'll try very hard to comment it all (for both your sake and mine). Again I got midterms coming up, and I've dodged studying enough already sooo this might be done a week or two from now...

Here is what I hope to accomplish/have so far:
    public class Nokia_5110
    {
        private OutputPort reset, dataMode;
        private PWM backlight;
        private SPI spi;

        public byte[] ByteMap = new byte[504];

        private uint _backlightVal = 0;
        private bool _invd = false;

        public Nokia_5110(Cpu.Pin latch = Pins.GPIO_PIN_D3, Cpu.Pin backlight = Pins.GPIO_PIN_D9,
            Cpu.Pin reset = Pins.GPIO_PIN_D7, Cpu.Pin dataCommand = Pins.GPIO_PIN_D8)
        {
            SPI.Configuration spiConfiguration = new SPI.Configuration(
                latch,            // chip select port
                false,                 // IC is accessed when chip select is low
                0,                     // setup time 1 ms
                0,                     // hold chip select 1 ms after transfer
                false,                 // clock line is low if device is not selected
                true,                  // data is sampled at leading edge of clock
                4000,                 // clockrate is 15 MHz
                SPI.SPI_module.SPI1   // use first SPI bus
                );

            spi = new SPI(spiConfiguration);
            
            this.backlight = new PWM(backlight);
            this.reset = new OutputPort(reset, true);
            this.dataMode = new OutputPort(dataCommand, true);

            Initialize();
        }

        private void Initialize()
        {
            reset.Write(false);
            reset.Write(true);

            dataMode.Write(false);
            spi.Write(new byte[] { 0x21, 0xBF, 0x04, 0x14, 0x0C, 0x20, 0x0C });
            dataMode.Write(true);

            Clear();
            Refresh();
        }

        public void WriteText(string text, bool noSpace = true)
        {
            if (noSpace)
                foreach (char c in text.ToCharArray())
                    spi.Write(NokiaCharacters.ASCII[c - 0x20]);
            else
            {
                byte[] letter = new byte[6];
                foreach (char c in text.ToCharArray())
                {
                    for (int i = 0; i < 5; i++)
                        letter[i] = NokiaCharacters.ASCII[c - 0x20][i];

                    letter[5] = 0x00;
                    spi.Write(letter);
                }

            }
        }

        public bool DrawPoint(short x, short y, bool on)
        {
            if (x < 0 || x >= 84 || y < 0 || y >= 48)
                return true; // out of the range! return true to indicate failure.

            ushort index = (ushort)((x % 84) + (int)(y * 0.125) * 84);
            
            byte bitMask = (byte)(1 << (y % 8));
            
            if (on)
                ByteMap[index] |= bitMask;
            else
                ByteMap[index] &= (byte)~bitMask;

            return false; // all is good (false = no error), return false to continue
        }

        public void DrawLine(short x1, short y1, short x2, short y2, bool on)
        {
            // This is a common line drawing algorithm. Read about it here:
            // http://en.wikipedia.org/wiki/Bresenham's_line_algorithm

            short sx = (x1 < x2) ? sx = 1 : sx = -1;
            short sy = (y1 < y2) ? sy = 1 : sy = -1;

            short dx = (short)((x2 > x1) ? x2 - x1 : x1 - x2);
            short dy = (short)((y2 > x1) ? y2 - y1 : y1 - y2);

            float err = dx - dy, e2;

            // if there is an error with drawing a point or the line is finished get out of the loop!
            while (!((x1 == x2 && y1 == y2) || DrawPoint(x1, y1, on)))
            {
                e2 = 2 * err;
                
                if (e2 > -dy)
                {
                    err -= dy;
                    x1 += sx;
                }

                if (e2 < dx)
                {
                    err += dx;
                    y1 += sy;
                }
            }
        }

        public void DrawRectangle(short X, short Y, short width, short height, bool on, bool filled)
        {
            // this is easier to do with points instead of lines since the line algorithm isn't that great.
            // this is only best to do with points because its straight lines. 

            short xe = (short)(X + width);
            short ye = (short)(Y + height);

            if (filled)
                for (short y = Y; y != ye; y++)
                    for (short x = X; x != xe; x++)
                        DrawPoint(x, y, on);
            else
            {
                xe -= 1;
                ye -= 1;

                for (short x = X; x != xe; x++)
                    DrawPoint(x, Y, on);

                for (short x = X; x <= xe; x++)
                    DrawPoint(x, ye, on);

                for (short y = Y; y != ye; y++)
                    DrawPoint(X, y, on);

                for (short y = Y; y <= ye; y++)
                    DrawPoint(xe, y, on);
            }
        }

        public void Refresh()
        {
             spi.Write(ByteMap);   
        }

        public void Clear()
        {
            ByteMap = new byte[504];
            dataMode.Write(false);
            spi.Write(new byte[]{ 0x80, 0x40});
            dataMode.Write(true);
        }

        private void Inverse(bool inverse)
        {
            _invd = inverse;
            dataMode.Write(false);
            spi.Write(inverse ? new byte[] { 0x0D } : new byte[] { 0x0C });
            dataMode.Write(true);
        }

        public bool InverseColors
        {
            get { return _invd; }
            set { Inverse(value); }
        }

        public uint BacklightBrightness
        {
            get
            { return _backlightVal; }
            set
            {
                if (value > 100)
                    value = 100;

                backlight.SetDutyCycle(value);
                _backlightVal = 100;
            }
        }
    }





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.