NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Making a library?

July 06, 2012
by bluestang
bluestang's Avatar

Has anyone started a thread where they explain how to make a library and how to store and use them once created?

i know that i need a .H file and .c file included but am not real sure how to create one form scratch. Thanks Jim

July 07, 2012
by pcbolt
pcbolt's Avatar

Jim -

I've read one or two threads on the subject on this forum, but they were mostly abstract discussions and Makefile questions. I'm assuming you want to create a library like the Nerdkits library of lcd.c, delay.c etc and not the full blown <stdio.h> type libraries.

The .c and .h files are text files like any other source code files. It is when you compile them that the differences show up. Take the delay.c source code file for instance. Since it does not have a "main()" function in it, it cannot be compiled into an executable program ( for AVR an executable has the .hex extension). What you can do though, is create an object file from it (.o extention). That is the basic goal of a making a library module...the object file. If you look at the "Makefile" inside the "libnerkits" directory you will see the command line you can use to create an object file from your source code file (i.e. text file). As an example if you hid the "delay.o" file from your computer, you can re-create it with this command line:

avr-gcc -g -Os -Wall -mmcu=atmega168 -o delay.o -c delay.c

Once you have an object file, you can link that object file into any program you want.

The .h header files are used when a new program wants to used previously created functions from that .h file. When you type "include delay.h" into your new source code (.c file) the compiler will search "delay.h" for the definition of any particular function. That definition includes the name of the function, the return type of variable and the types of arguments supplied to that function. From there the comiler can create an object file from your new .c file. In the final step, you will need the delay.o file to link with your new .o file to create the final .hex file.

Hope I didn't muddy the question too much.

July 07, 2012
by bluestang
bluestang's Avatar

Thanks PCbolt, I am trying to learn several things at the same time and it occurred to me that it would be good to know how to create a library to store items to use in several projects...

July 11, 2012
by pcbolt
pcbolt's Avatar

I agree completely. I don't consider a project finished until I have a working 'library' that incorporates all the code needed to operate a certain piece of hardware. I just finished making a 2-wire shift register interface for the LCD screen and all I had to do was modify the Nerdkit library a little bit and use the same function calls the previous projects used. BTW it is really convenient to only need to hookup 2 wires instead of 6 when changing the LCD from one project to another (saves some valuable port pins too). Good luck on your project!

July 11, 2012
by Ralphxyz
Ralphxyz's Avatar

Ahem,

I just finished making a 2-wire shift register interface for the LCD screen

Gee, what a good article that would make in the Nerdkit Community Library.

I use Ricks_S's I2C LCD pcb Backpack that he had made up.

With his I2C LCD library.

It is really great to have such a clean breadboard and to have PORTD available.

It would really be interesting to have a shift register method in the library so that there is a ready reference to the two 2 wire LCD methods.

I imagine the shift register would be faster than I2C.

Ralph

July 11, 2012
by pcbolt
pcbolt's Avatar

Ralph -

I got most of the information HERE. Zoran (aka Wayward) put together a great webpage describing the nuts and bolts of the project (he posted a link on the forum page). The really interesting part is how to get away with using only two pins and one diode/resistor pair to create an AND gate. My only changes to the project are using a dedicated high-speed shift register instead of a flip-flop, and I only needed to modify a small section of the original Nerdkit LCD code. I'm on a business trip now, but when I get back, I'll try to post a pic or two along with the modified portions of the code.

July 19, 2012
by pcbolt
pcbolt's Avatar

Ralph -

Here are the pics I promised...

Close Up

Working

July 20, 2012
by Ralphxyz
Ralphxyz's Avatar

pcbolt, nice neat proto board.

You must have drastically changed something to get that <= 120.

That large font is rather attractive.

I'd like to see that on my 4x40 LCD.

Ralph

July 20, 2012
by pcbolt
pcbolt's Avatar
  • "You must have drastically changed something to get that <= 120"

Actually, the funny thing is, to make the large digits you only need to introduce 3 new characters in the LCD RAM. All the digits can be made from these 3 new "characters", a blank space (0x20) and the "full" character (0xff). The arrows took up more LCD RAM than the digits (4 new characters for left and right arrows). I'll post some code when I get a chance.

July 20, 2012
by pcbolt
pcbolt's Avatar

Here you go Ralph -

Start with some includes:

#include <inttypes.h>
#include <avr/pgmspace.h>
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/delay.h"

Next I just charted out the individual characters the way I wanted them to look (this isn't code just a comment section) and assigned a hex value for each bit pattern:

/******************************************************************************
    CGRAM bitmap patterns for LCD
    // NUMBERS
    // Symbol "1"         // Symbol "2"          // Symbol "3"   
    (0b00011111) 0x1F     (0b00000000) 0x00  (0b00011111) 0x1F
    (0b00011111) 0x1F     (0b00000000) 0x00  (0b00011111) 0x1F
    (0b00000000) 0x00     (0b00000000) 0x00  (0b00000000) 0x00
    (0b00000000) 0x00     (0b00000000) 0x00  (0b00000000) 0x00
    (0b00000000) 0x00     (0b00000000) 0x00  (0b00000000) 0x00
    (0b00000000) 0x00     (0b00000000) 0x00  (0b00000000) 0x00
    (0b00000000) 0x00     (0b00011111) 0x1F  (0b00011111) 0x1F
    (0b00000000) 0x00     (0b00011111) 0x1F  (0b00011111) 0x1F

    // ARROWS
    // Symbol "4"          // Symbol "5"          // Symbol "6"      // Symbol "7"
    (0b00000001) 0x01   (0b00011111) 0x1F     (0b00010000) 0x10  (0b00011111) 0x1F
    (0b00000001) 0x01   (0b00001111) 0x0F     (0b00010000) 0x10  (0b00011110) 0x1E
    (0b00000011) 0x03   (0b00000111) 0x07     (0b00011000) 0x18  (0b00011100) 0x1C
    (0b00000011) 0x03   (0b00000111) 0x07     (0b00011000) 0x18  (0b00011100) 0x1C
    (0b00000111) 0x07   (0b00000011) 0x03     (0b00011100) 0x1C  (0b00011000) 0x18
    (0b00000111) 0x07   (0b00000011) 0x03     (0b00011100) 0x1C  (0b00011000) 0x18
   (0b00001111) 0x0F    (0b00000001) 0x01     (0b00011110) 0x1E  (0b00010000) 0x10
    (0b00011111) 0x1F   (0b00000001) 0x01     (0b00011111) 0x1F  (0b00010000) 0x10
*********************************************************************************/

Then I packed the hex codes into program memory to save MCU RAM space:

uint8_t symbols[] PROGMEM = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,                   // Symbol "1"
                                            0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,                // Symbol "2"
                                            0x1F,0x1F,0x00,0x00,0x00,0x00,0x1F,0x1F,                // Symbol "3"
                                            0x01,0x01,0x03,0x03,0x07,0x07,0x0F,0x1F,                // Symbol "4"
                                            0x1F,0x0F,0x07,0x07,0x03,0x03,0x01,0x01,                // Symbol "5"
                                            0x10,0x10,0x18,0x18,0x1C,0x1C,0x1E,0x1F,                // Symbol "6"
                                            0x1F,0x1E,0x1C,0x1C,0x18,0x18,0x10,0x10};               // Symbol "7"

Now you just need the character pattern to go with each numeral you are printing. There are 3 horizontal characters per numeral and 2 vertical so they can be split apart into "top" and "bot" sequences (again packed away nicely into program space):

uint16_t top[] PROGMEM = {0x0919,0x0190,0x0339,0x0139,0x0929,0x0933,0x0933,0x0119,0x0939,0x0939};
uint16_t bot[] PROGMEM = {0x0929,0x0292,0x0922,0x0229,0x0009,0x0229,0x0929,0x0009,0x0929,0x0229};

As an example, take the second entry in "top[]" which is the printing sequence for the top of numeral "1" is 0x0910. As you will see later, the first "nibble" 0 is ignored, the second nibble 9 is code for the "all black" character (0xff), the third nibble 1 will print the custom "Symbol 1" above and the fourth nibble 0 will print a space character (0x20).

The first function used is for storing the bit patterns into the LCD CGRAM and assigning them a character code:

void init_big_num(){

    uint8_t i, j, value;

    for (j = 0; j < 7; j++){
        lcd_set_type_command();
        lcd_write_byte(0x40 | ((j + 1) << 3));      // assign char code # (j + 1) in CGRAM
        lcd_set_type_data();
        for (i = 0; i < 8; i++){
            value = pgm_read_byte(&(symbols[(j * 8) + i]));
            lcd_write_byte(value);
            delay_ms(1);
        }
    }

    return;

}

To print a numeral at a specific location you call this function:

void draw_big_digit(uint8_t row, uint8_t col, uint8_t digit){

    int8_t i, symbol;

    if (digit > 9) digit = 0;
    if (row > 2) row = 2;
    if (col > 17) col = 17;

    lcd_goto_position(row, col);            // write top row
    for (i = 0; i < 3; i++){
        symbol = ((pgm_read_word(&(top[digit]))) >> ((2 - i) * 4)) & 0x0f;
        if (symbol == 9) symbol = 0xff;
        if (symbol == 0) symbol = 0x20;
        lcd_write_data(symbol);
    }

    lcd_goto_position(++row, col);      // write bottom row
    for (i = 0; i < 3; i++){
        symbol = ((pgm_read_word(&(bot[digit]))) >> ((2 - i) * 4)) & 0x0f;
        if (symbol == 9) symbol = 0xff;
        if (symbol == 0) symbol = 0x20;
        lcd_write_data(symbol);
    }
    return;
}

This just unpacks the sequence of symbols and prints them. I made the "draw arrows" functions easier:

void draw_left_arrow(uint8_t row, uint8_t col){

    if (row > 2) row = 2;
    if (col > 17) col = 17;

    lcd_goto_position(row, col);
    lcd_write_data(4);
    lcd_write_data(2);
    lcd_write_data(2);
    lcd_goto_position(++row, col);
    lcd_write_data(5);
    lcd_write_data(1);
    lcd_write_data(1);

    return;
}

void draw_right_arrow(uint8_t row, uint8_t col){

    if (row > 2) row = 2;
    if (col > 17) col = 17;

    lcd_goto_position(row, col);
    lcd_write_data(2);
    lcd_write_data(2);
    lcd_write_data(6);
    lcd_goto_position(++row, col);
    lcd_write_data(1);
    lcd_write_data(1);
    lcd_write_data(7);

    return;
}

Feel free to use this for your 4x40 LCD. I created it to use outdoors at a distance to guide a (slow moving!) vehicle operator to drive a straight line at the right location.

July 20, 2012
by pcbolt
pcbolt's Avatar

Typo alert!

  • "As an example, take the second entry in "top[]" which is the printing sequence for the top of numeral "1" is 0x0910. As you will see later, the first "nibble" 0 is ignored, the second nibble 9 is code for the "all black" character (0xff), the third nibble 1 will print the custom "Symbol 1" above and the fourth nibble 0 will print a space character (0x20)."

Should read:

  • "As an example, take the second entry in "top[]" which is the printing sequence for the top of numeral "1" is 0x0190. As you will see later, the first "nibble" 0 is ignored, the second nibble 1 will print the custom "Symbol 1" above, the third nibble 9 is code for the "all black" character (0xff) and the fourth nibble 0 will print a space character (0x20)."

Post a Reply

Please log in to post a reply.

Did you know that binary numbers use base 2 to represent numbers, and these are important for understanding microcontroller registers? Learn more...