Skip to main content

Libraries, what they really are


This post is more of an introduction to what I plan to cover further on in the future.

Let's start with the basics, for those who are not very familiar with C compilation details. How to create a library, what kind of libraries can we create, what are the advantages/disadvantages ? First of all, there are two types of libraries, static (*.a files) and dynamic libraries (*.so files). The first ones are just a bunch of object files glued together to form a single file (object archive). The second ones are loaded during runtime by operating systems (like Linux (*.so files) /Windows (*.dll files)) whenever applications need them. Both have their pros & cons.

The advantage of dynamic libraries, short summary:
  • The library code is NOT included in every application using the library, the resulting executables are smaller
  • The library is loaded to memory by operating system (dynamic library loader) once an application requests it, and removed when no longer needed.
  • The library is shared across many applications - only one copy exists in the memory
  • When making changes to the library there is no need to recompile once again every application using it (as long as the interface stays compatible)

Disadvantages:
  • A true multi - process capable operating system is needed

In contrast to the dynamic libraries static libraries are simply compiled into the executable itself for every application using the library. The only advantage we really gain, is that once the library is compiled, we do not have to compile it anymore (if it doesn't change), we just compile the code of our app and link against the library.

The advantages of static libraries, short summary:
  • User results with a single opaque object archive file (.a) which he needs to link his application against
  • The library is an easily decoupled logical entity, containing some universal code
  • No sophisticated runtime loading mechanisms are required since the library code is included in the application itself

Disadvantages:
  • Every application must link library code into itself (larger executables)
  • Every application using the library must be recompiled if we incorporate a new change to the library

Although dynamic libraries seems to be far more superior, we can only benefit from creating static libraries for embedded systems like Arduino though. Now why is that ? The answer is simple: No operating system ! On our little 8 bit machine there are no resources to run an operating system capable of managing dynamic libraries and real multiprocess environment, thus the only option available are static libraries.

Just as an exercise let's create a dummy library. We'll need two files dummy.c and dummy.h
and the code:
We can't go any simpler. To create a library out of this code, all we need to do is compile it as an object:

avr-gcc -mmcu=atmega328p -I. -c dummy.c

In fact this single object file is already a static library. The (.a) file only aggregates them into a single file nothing else.

avr-ar rcsv libdummy.a dummy.o

... and there we go, our first lib. It can be used in a program the following way:

avr-gcc -mmcu=atmega328p main.c -ldummy -L. -I.

Additionally, we can have a look what symbols are inside:

avr-nm libdummy.a

dummy.o:
0000003e a __SP_H__
0000003d a __SP_L__
0000003f a __SREG__
00000000 a __tmp_reg__
00000001 a __zero_reg__
00000000 T portb_high

Our function starts from address 0x00, we can have a look at the assembler output:

In archive libdummy.a:

dummy.o:     file format elf32-avr


Disassembly of section .text:

00000000 <portb_high>:
   0: cf 93       push r28
   2: df 93       push r29
   4: cd b7       in r28, 0x3d ; 61
   6: de b7       in r29, 0x3e ; 62
   8: 85 e2       ldi r24, 0x25 ; 37
   a: 90 e0       ldi r25, 0x00 ; 0
   c: 2f ef       ldi r18, 0xFF ; 255
   e: fc 01       movw r30, r24
  10: 20 83       st Z, r18
  12: df 91       pop r29
  14: cf 91       pop r28
  16: 08 95       ret


What can be seen here ? First, registers r28,r29 (register Y) are preserved on the stack (address 0 and 2). Then, r28,r29 is configured to the current stack pointer top. That's a standard gcc behavior. Next two instructions load registers r24,r25 with the memory address of PORTB (0x25). The instruction at address 0x0c loads our argument 0xff to register r18. Next two instructions copy the register pair r24,r25 to r30,r31 (register Z) in order to store the value in r18 (0xff) in the memory (0x0025 - PORTB). Next the stack is restored and we exit the function. Quite a lot of stuff as for such a simple code :). This code is compiled of course without any optimizations enabled. If we compile it once again with -Os (size optimization), the output looks a lot less clobbered:

In archive libdummy.a:

dummy.o:     file format elf32-avr


Disassembly of section .text:

00000000 <portb_high>:
   0: 8f ef       ldi r24, 0xFF ; 255
   2: 85 b9       out 0x05, r24 ; 5
   4: 08 95       ret

The code is pretty obvious and doesn't require further explanations from my point of view. At this time I'm sure that most of the library technical specific aspects have been covered and we are ready to create some more useful stuff. In the next post, I'll try to explain the details regarding the Atmega's Timers and introduce you to a real library with more useful code.

Comments

Popular posts from this blog

RTC without RTC ? How good is my crystal ?

Clock - I think that it's the most popular idea for a project with the MCU . It's not that hard from both software and hardware side to realize but not that trivial as well, not mentioning the fact that once realized it sits somewhere in the house, constantly reminding about itself and brings a kind of satisfaction :). There are a lot of options really to implement, a buzzer alarm, perhaps a thermometer, display and buttons servicing, perhaps maybe even serial port usage for NTP synchronization. But ... The most challenging thing - like in every clock is the time reference source. There are a couple of options: well calibrated frequency source acting as an interrupt source Real Time Clock module with external crystal ( < 20 ppm ) internal clock These days RTC chips are so cheap and widely available that they are really the only reasonable choice, but if we're only planning to play around a bit with the software without paying much attention to accura...

Arduino R-2R ladder Audio DAC

There is a lot of projects out there which use  R-2R ladder and an Arduino to recreate sounds from either SD card or short audio clips programmed directly to MCU's flash memory. Although using SD card is fairly reasonable and provides a lot of flexibility it's not very challenging (from the software point of view) and it requires some additional hardware. Believe it or not we already have all the needed hardware in the Arduino itself. Assumptions In this project I'll play mp3 or any other multimedia files from the PC using Arduino. Bellow are the details: Play PCM 8kHz 8bit Audio with Arduino Audio samples will be transfered via USART from the PC in binary format using SLIP Audio files will be decoded on the PC side and only the RAW data will be send to Arduino Timing Arduino is a powerful machine, powerful enough that it's possible to play audio with even higher sampling rates and bit-resolutions than assumed above, the bottleneck in this...

Simple Serial Port Command Line Interface (CLI)

It's often very useful to have some sort of interface through which a basic management can be done of our application. Whether you're designing a giant Marquee with a LED display or a plotter or you simply need to get some diagnostics from your device occasionally, a simple CLI system can come very handy.  Of course, designing something similar to "bash" or any other unix shell is completely out of scope since those applications are huge and are simply an overkill for our needs. It's pretty simple though to create some basic, yet flexible & easilly extensible CLI system. First thing needed is a command type definition. This will bind a keyword with an actual underlying routine executed for that keyword typed in the CLI . typedef struct _t_cmd { const char *cmd; void (*fh)(void*); } t_cmd; The command type is pretty simple. There's the CLI command/keyword pointer, it holds a pointer to the execution function and that's it. OK, so...