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.

Support Forum » Create a library

November 09, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi all!

Hope everyone that is still utilizing these forums is doing well. It has been a while again...man life just doesn't seem to slow down much right now.

Anyways,

I have been looking at creating my own library for some of the sensors/devices that I have written code for, ie. BMP085 pressure sensor, EEPROM. However, I have a couple of questions/thoughts.

How does one go about building something like the libnerdkits library? Are there any resources that can be found on the subject? I have read through:

This tutorial

However, it seems to leave out quite a bit of information.

Are there certain requirements, as in is lib a required prefix before the library name?

Also, looking at the uart.h and uart.c files in the libnerdkits folder:

first, uart.c

    // uart.c
    // for NerdKits with ATmega168, 14.7456 MHz clock
    // mrobbins@mit.edu

    #include <stdio.h>
    #include <stdlib.h>

    #include <avr/io.h>
    #include <inttypes.h>
    #include "uart.h"

    void uart_init() {
      // set baud rate
      UBRR0H = 0;
      UBRR0L = 7;   // for 115200bps with 14.7456MHz clock
      // enable uart RX and TX
      UCSR0B = (1<<RXEN0)|(1<<TXEN0);
      // set 8N1 frame format
      UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);

      // set up STDIO handlers so you can use printf, etc
      fdevopen(&uart_putchar, &uart_getchar);
    }

    void uart_write(char x) {
      // wait for empty receive buffer
      while ((UCSR0A & (1<<UDRE0))==0);
      // send
      UDR0 = x;
    }

    uint8_t uart_char_is_waiting() {
      // returns 1 if a character is waiting
      // returns 0 if not
      return (UCSR0A & (1<<RXC0));
    }

    char uart_read() {
      // wait
      while(!uart_char_is_waiting());
      char x = UDR0;
      return x;
    }

    int uart_putchar(char c, FILE *stream) {
      uart_write(c);
      return 0;
    }

    int uart_getchar(FILE *stream) {
      int x = uart_read();
      return x;
    }

And uart.h

    #ifndef __UART_H
    #define __UART_H

    #include <inttypes.h>
    #include <stdio.h>

    FILE mystream;

    void uart_init();
    void uart_write(char x);
    uint8_t uart_char_is_waiting();
    char uart_read();

    int uart_putchar(char x, FILE *stream);
    int uart_getchar(FILE *stream);

    #endif

Why does the .h file and .c files both have some of the same include files? They both include inttypes and stdio.

I thought that the header basically was read and took care of all the include files. Is this not the case?

Or is it because two of the prototypes defined in uart.h are declared integers? It just seems redundant.

Do I need to "make" the library with a makefile? Or is there a way to do it manually? Does anyone have any advice or a "formula" for making libraries?

Again,

Thanks for sticking in here, I appreciate all of your help!

Dave

November 10, 2013
by Ralphxyz
Ralphxyz's Avatar

Hi Dave, one of the key points from the article is:

  • but it is ten times more important to remain consistent in your scheme across your entire project.

That's the one that has caught me in the past.

What do you see as missing?

Ralph

November 10, 2013
by Noter
Noter's Avatar

Putting .c source and .h includes into a common folder does not make a library. It is just a common directory where reusable code may reside. A true library is a file containing precompiled objects. Although there exists both static and dynamic linking, in the world of the nerdkit we deal only with static linking. With a real library, the linker will reference only the library file instead of one or many individual objects.

So the libnerdkits directory is just a common directory. It can be named anything you wish as the name doesn't make any difference. Likewise, a library file, typically a .o, can be named anything you wish. It's what's inside that makes the difference.

From an organizational perspective it is good to have a common place for reusable code to reside. This is particularly helpful in larger projects with many .c and .h files used by many other .c files. In the tutorial above, Dean is showing a method that helps to organize and manage reusable code. At the very end he says you may want to put the common objects in a library for distribution. His method of organization is not a library, it is just one way to have a common directory for reusable code. Another way is to put all your reusable code in a directory named libnerdkits. It could just as easily be named common or share.

November 10, 2013
by Noter
Noter's Avatar

As for the include<avr/whatever>, it doesn't really matter to the compiler how many times they are included because it only uses the first one it comes across. Hard to say why some authors put/leave useless includes into header or source files.

November 10, 2013
by JKITSON
JKITSON's Avatar

NOTER, Thanks for the information. This helped me to understand the naming concept better..

Jim

November 11, 2013
by dvdsnyd
dvdsnyd's Avatar

Noter,

As always you really help me(and everyone else) understand things easier.

So, how does one go from just "managing" reusable code in a common directory to a full blown library? (I would imagine some sort of a makefile leading to a .o file correct?

Or is it not necessary to go through the trouble?

Thanks again for your help!

Dave

November 11, 2013
by Noter
Noter's Avatar

I was building and using a library for a while but now just directly link the objects again. I don't use a lot of shared code so it doesn't make much difference to me. Usually I just put code for the usart in the program I'm writing because it's just a couple of small functions and it's easier to change them there. I use the avr-gcc util/delay's now and seldom use the lcd display and typically my program has no includes from a common directory. Makes it easier to send it or post it for someone else too.

I said above the library was a .o and the name doesn't matter. After a quick review of the documentation I realize I forgot it's a .a and the name must begin with lib. Here's the makefile I used to build the nerdkits library -

#
# compile all c source and insert objects into a library file (libNerdkit.a)
#
all: $(patsubst %.c,%.o,$(wildcard *.c))

%.o: %.c %.h
    #avr-gcc -g -O0 -c -Wall -mmcu=atmega168 $< -o $@
    avr-gcc -g -O0 -c -Wall -mmcu=atmega328p $< -o $@

    avr-ar rcsv libNerdKit.a $@

clean: 
    -rm *.o

Then to use it in a make file this is the rule and recipe I used -

%.o: %.c ../libnerdkits/libNerdKit.a Makefile
    avr-gcc -g -Os -Wall -mmcu=$(MCU) -mcall-prologues \
        -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm \
        -Wl,-Map=$@.map \
        $< -o $@ ../libnerdkits/libNerdKit.a \
        -DF_CPU=$(F_CPU) -DBAUD=$(PGM_BAUD)

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...