Mbed LCD Driver for N18 display in the works

That is correct, each array is a single bitmap. The first two entries represent the width and height of the bitmap and the rest of the entries represent the pixel data,. So the 3 images are pacman with his mouth closed, partially open and fully open.

Added support for drawing ellipses and filling ellipses

2 Likes

@ taylorza - Thank you for posting this information. I got your code working and have a couple of questions. I have not programmed in c so I have conceptual and syntax questions. The asterisk means you are passing a pointer? In Marque you call drawstring then put the first character into the char c variable. I suspect I could then pass c as a pointer as well. In the line starting with memcopy you copy the range of memory starting at msg +1 with a length of strlen(msg) - 1 to msg. So I gather I can add integer values to a pointer? You then put c onto the end of the range of memory pointed to by msg.

Is the area for variable storage pretty large? I guess Frames 1 thru 3 really aren’t that big but is it feasible to store a bunch of images like that?

Do you use software to generate the values in the frame arrays? Creating those manually seems painful.

@ Bill_Wyo - I will try give some basic answers to your questions and in some cases this is going to be an over simplification, but it should not hurt too much until you get further down the road with you C/C++ experience at which point you should know enough to realize where I have over simplified :slight_smile:

The use of the star or asterisk in C/C++ is used to denote different things depending on the context.


// Declare a variable p which value is a pointer to a memory 
// address who's content is interpreted as a single byte character
char *p;

The above can be a local stack variable an argument to a function, a global variable etc. In any case, you are not dealing with the actual object (in this case the char) but a pointer to a memory location containing a char.

Since ‘p’ in the above example is really just a numeric memory address you can manipulate that address as a numeric number, so for example if you have


p += 10;

‘p’ will now point to an address 10 chars further into memory. The critical thing in the terminology in that previous sentence is that it is 10 ‘chars’, if the pointer ‘p’ had been a pointer to a 4 byte integer then the same code above would have put ‘p’ pointing to an address 10 integers or 40 byte further into memory, the C/C++ compiler automatically does the address calculation based on the size of the object you are pointing to. In the case of a char that is only 1 byte in size the address is adjusted forward by 10 bytes.

No you know how to declare a variable and manipulate where the variable is pointing how do you actually manipulate the data at that memory location. Here we have two possible syntaxes depending on the object we are pointing to, I am just going to consider our simple case where we are pointing to a primitive value like char or int etc.

Assuming ‘p’ now points somewhere in memory (we will talk about how this happens shortly) we can now put a character in that memory cell by doing the following


*p = 'C';

This syntax dereferences the pointer ‘p’ so that we can gain access to the content pointed to by the pointer, so we are saying assign the char ‘C’ to that memory location.

Conversely if we want to read the content of a pointer we do the following


char myChar = *p;

This again dereferences the pointer ‘p’ and gains access to the content of the memory location which is then assigned to our variable ‘myChar’.

So how do we find a free address to initially point our variable ‘p’ to. Well that depends on what you want to do.

We could have the address statically assigned by the compiler. This is common when you are working with strings.


char *p = "Hello World";

In the above, the compiler will put the string of characters for “Hello World” in the data section (and conveniently add an null terminator (ie. ASCII code 0) at the end of that string, since C/C++ treat these string as null terminated strings. ‘p’ will point to the first character of the string and you can step through each character using something like


// create another pointer 'np' that points to the same location as 'p'
char *np = p;
while (*np++ != 0)
{
  // Do something with *np
}

*np++ will access the current character pointer for by np (deference the pointer) and increment the pointer to the next character. Each time checking if the content is not the 0 terminating code. We made a copy of the pointer ‘p’ because we might need to get to the start of the string again so we did not want to change the address that ‘p’ points to, we just change the address our copy points to and use that in our logic.

Another way to allocate the memory is to use the ‘new’ operator, this is C++ only


char *p = new char[10];

This will allocate a block of memory that is 10 chars in size (if this have been a 4 byte integer then it would be a block of 10 integers ie. 40 bytes of memory). that you can reference through the pointer ‘p’. This memory is dynamically allocated at runtime by the C++ memory allocator which is part of the runtime library (can be customized etc. but that is way more than we need to discuss now), what is important is that it will find a sufficiently large block of memory to allocation and return the pointer, if there is no memory available it will fail and return ‘NULL’ which you should check for to see if you memory allocation succeeded. There is a lot to talk about regarding memory allocation and how it can fail, even whether it returns NULL or if an exception is thrown, but these are the basics.

In C/C++ any memory allocation should typically be paired with a corresponding deallocation, if you no longer need the memory you should release it so that it is available for other areas of your system to use.


delete []p;

Notice that the delete operator frees memory allocated by new, in this case we used the array delete operator ‘delete []’ because our initial allocation was allocating an array of chars and not just a single char.

Another way to allocate memory for a pointer is to use malloc, this is a function from the C libraries and while it can be used in C++ there are limitations I would suggest you do not unless you know what you are doing, for example if you use malloc to allocate memory for a C++ object then the object will not be automatically initialized while with the ‘new’ operator’ the constructor will be run etc. This is also more complex than we need to discuss, but I do want to leave you with some pointers (pun not intended) that you can go research further.

Regardless, we could allocate our char array using malloc as follows


char *p = (char*)malloc(10 * sizeof(char));

Technically we do not need the sizeof(char) portion, but I wanted to show that malloc only allocates the specified number of byte passed to the function, if you know your objects used more than 1 byte per instance then you need to do the calculation your self. So allocating 10 4 byte integers would be


int *pint = (int*)malloc(10 * sizeof(int));

The ‘sizeof’ operator is convenient in that we do not need to worry about the size of the type, the compiler will take care of that for us. But it has restrictions so it is another good thing to go read more about.

Freeing memory allocated by ‘malloc’ is simple, just call the ‘free’ function and pass the original pointer address returned by malloc. With malloc/free there is no distinction between allocating memory for a single instance or memory for an array of instances, the block is allocated and the block is freed.

The following will release the memory allocated earlier with malloc.


free(p);

Note: You cannot mix new/malloc/delete/free, memory allocated with new must be freed using delete and memory allocated with malloc must be freed using free. Otherwise a whole host of things can and will go wrong, depending of the types of objects and what they do it could be as simple as having a memory leak which eventually crashes the system to things acting strangely and misbehaving in a seemingly random and untraceable way.

As for the frames, I just wrote a little application that converts standard windows bitmaps to the image format I supported for bitmaps in the LCD library. So those images are the bitmaps I have for my NETMF pacman game that I quickly converted to the binary format.

By making those arrays as constant the compiler will put those arrays into a readonly memory section which is then loaded to the flash rather than to RAM. The advantage if this is that there is far more flash memory (32K) than RAM (8K) on the mBuino’s LPC11u24 processor so if you have static data it is always best to convince the compiler to push that to flash memory and use your RAM for memory that changes.

I hope I have been able to address most of your questions in a reasonably comprehensible manner, if I have missed anything or anything requires further clarification, please shout. I hope my over simplifications and omissions do not leave you too much in the dark.

6 Likes

@ taylorza - It is going to take some time for me to assimilate all that you wrote. Thank you for your time. I appreciate it.

@ taylorza - Great answer! Wasn’t even my question, and I learned a lot from it.

Someone give this man a point bonus!!! :slight_smile:

It does take a while, but it is not overly complex once you find that final piece of the puzzle that makes everything just fall into place.

If you need a hand with anything C/C++, stackoverflow is a great resource, but if you post here I and I am sure others more qualified would be more than happy to help out.

@ devhammer, thank you.

Finally got a chance to test the library…very nice work. Got it working on the 2nd or 3rd try, and initial failures had nothing to do with the library. In fact, testing your library helped me discover that I had a bad breakout module. Once I replaced that, it worked perfectly.

Great job!

1 Like

Is the bad breakout module the reason of struggle with I2C?

Exactly right…just posted about that on the other thread.

Must be either pin 8 or 9 on the makebread module, because those are the only two used for the I2C comms.

Now there’s a name I haven’t seen here in awhile. Makes me want to go knead some dough… :slight_smile:

Heh.

Turns out, BTW, that it was the ground pin that was problematic, perhaps due to a bad diode. Reads pretty low on the diode tester setting on my EX210 multimeter…should read 0.400v to 0.700v according to the manual, but I’m only getting 0.185v.