The EE Compendium

 

 

The EE Compendium is relocating to a new home!


The EE Compendium

The new site has been completely redesigned for easier navigation, and contains many new categories, including more microcontroller information and an expanded projects section. Please visit the the redesigned site and update your bookmarks. The new address is: http://www.eecompendium.com/

 

 

Embedded C Code Cache


Here's a collection of various bite-sized bits of C code that I've written and accumulated over time. This code was written for embedded systems that employed various processors and microcontrollers, using various compilers, but most of it should be fairly portable. If you find anything useful, grab it.


SPI Routines

This code is used to communicate via the SPI (Serial Peripheral Interface) on the 68HC11 (but should also work with the 6805). It was written for the IAR compiler, but should work with others. It assumes that the HC11 registers have been defined with their standard names.

There's not much to this. Call SPI_Init() during system initialization. Call SPI_Transfer() to simultaneously send and receive a byte of data. If you want to send only, and ignore the received byte, precede the name of the function with a (void) when you call it, to tell the compiler that you're purposely ignoring the return value.

    /*
    *********************************************************************************************************
    *
    * SPI_Init()
    *
    * Description: SPI initialization function.  SPI_Init() must be called
    *              before calling any other SPI functions.
    * Arguments  : none
    * Returns    : none
    *********************************************************************************************************
    */
    void SPI_Init (void)
    {
      SPCR = 0x5d;    // initialize SPI Control Register
                      //  (7) SPIE = 0 -- disable SPI interrupts
                      //  (6) SPE  = 1 -- enable SPI system
                      //  (5) DWOM = 0 -- normal CMOS outputs (not wired-or)
                      //  (4) MSTR = 1 -- this is the SPI master
                      //  (3) CPOL = 1 -- SCLK is high in idle state
                      //  (2) CPHA = 1 -- data is clocked on rising edge of SCLK
                      //  (1) 0 \ SPI clock rate = E / 4
                      //  (0) 1 /   (500KHz @ 2MHz E clock)
    }
    
    
    /*
    *********************************************************************************************************
    *
    * SPI_Transfer()
    *
    * Description : Transfer data to/from the SPI port
    * Arguments   : data to send
    * Returns     : received data
    *********************************************************************************************************
    */
    unsigned char SPI_Transfer (unsigned char data_out)
    {
      SPDR = data_out;              // send data out spi port
      while (!(SPSR & 0x80));       // wait for transfer to complete
      return = SPDR;                // return data from spi register
    }
    
    


long_to_string()

This code is used to convert an unsigned long (32 bits) to an ASCII string suitable for displaying. It is intended as a replacement for the standard library ltoa() function, for cases where your compiler doesn't supply it, or it's slow and/or fat. This code was written for the 68HC11 and the IAR compiler, but should be fairly generic.

Call long_to_string() to convert a long to a string, with leading zeros suppressed. If you want leading zeros, use long_to_string_lz(). These two functions could be combined into a single function, but it would require an extra parameter. Be sure to allocate enough space for the resulting string before calling these functions. Failure to do so will cause bad things to happen.

    /*
    *********************************************************************************************************
    * long_to_string()
    *
    * Description : Convert a "long" to a null-terminated string
    *               (base = decimal)
    * Arguments   : input = number to be converted
    *               str = pointer to string (i.e. display buffer)
    *               numdigits = number of digits to display
    * Returns     : none
    *********************************************************************************************************
    */
    void long_to_string (unsigned long input, char *str, char numdigits)
    {
      char digit;
      char blank = TRUE;
    
      long_to_string_lz(input, str, numdigits);
    
      for (digit=0; digit  0; digit--) {
        str[digit-1] = (input % 10) + '0';
        input = input / 10;
      }
      str[numdigits] = 0;    // null-terminate the string
    }
    
    


uword_to_hexstring() and hexstring_to_uword()

This code is used to convert an unsigned integer (16 bits) to an ASCII hexidecimal string suitable for displaying. Note that this only creates the digits -- if you want a leading "0x" or "$", it's up to the calling function to add it. This code was written for the 68HC11 and the IAR compiler, but should be fairly generic.

Be sure to allocate enough space for the resulting string before calling uword_to_hexstring(). Failure to do so will cause bad things to happen.

hexstring_to_uword() is simply the inverse of uword_to_hexstring(). Given a string of hex digits (0-9, A-F, a-f), it converts them intil an unsigned integer. Note that it requires the standard library function isxdigit().

    /*
    *********************************************************************************************************
    * uword_to_hexstring()
    *
    * Description : Convert an unsigned int to a null-terminated string
    *               (base = hexidecimal)
    * Arguments   : input = number to be converted
    *               str = pointer to string (i.e. display buffer)
    *               numdigits = number of digits to display
    * Returns     : none
    *********************************************************************************************************
    */
    void uword_to_hexstring (unsigned int input, char *str, char numdigits)
    {
      char digit;
    
      for (digit=numdigits; digit > 0; digit--) {
        char temp = input % 0x10;
        if (temp <= 0x09) str[digit-1]=temp + '0'; else str[digit-1]=temp - 0x0a + 'a'; input=input / 0x10; } str[numdigits]=0; // null-terminate the string } /* ********************************************************************************************************* * hexstring_to_uword() * * description : convert a null-terminated string (base=hexidecimal) * to an unsigned int (16 bits); * arguments : str=pointer to string * returns : result * note : adapted from a function in the "Snippets" collection, by bob stout. ********************************************************************************************************* */ unsigned int hexstring_to_uword (char *str) { unsigned int i, j=0; while (str && *str && isxdigit(*str)) { i=*str++ - '0'; if (9 < i) i -=7; j <<=4; j |=(i & 0x0f); } return(j); } 


LCD Routines

Refer to Randy's LCD Project Page for a nifty little project to control a character-mode LCD from the PC printer port. It includes a schematic and code (LCD_4X20.ZIP). The code was written for Borland C, but should be easily adaptable for most compilers. I'm using essentially the same code in an HC11 project, to control a 4x20 LCD.


MAX7219 LED Display Driver Routines

The Maxim MAX7219 is an LED display driver that can control up to 64 individual LEDs, or eight 7-segment LED digits, or any combination of individual LEDs and digits, connected in a matrix of eight rows and eight columns. It frees the host from the chore of constantly multiplexing the 8 rows and 8 columns. In addition, it takes care of brightness control (16 steps), and implements display test and display blank (shutdown) features.

The host communicates with the MAX7219 using three signals: DATA, CLK, and LOAD. This module bit-bangs them, but Motorola's SPI interface (or similar interface from other manufacturers) may also be used to simplify and speed up the data transfer.

This module was written and tested using an Atmel AT89C2051 microcontroller, with the MAX7219 connected to I/O pins P3.3 (LOAD), P3.4 (CLK), and P3.3 (DATA). The Hi-Tech 8051 C compiler was used to test the code.

The code is provided in an archive (MAX7219.ZIP). that contains MAX7219.C and MAX7219.H. To use the module in your program, simply #include MAX7219.H from wherever you need to call the functions, and link MAX7219.C into your program.

Call MAX7219_Init() during program initialization, then call MAX7219_DisplayChar() to display a digit, passing it the digit number (position within the display and the ASCII digit which should be displayed. As is, the module only displays 0-9, A-F, and a blank, but can easily be modified to display other letters and symbols.


Xicor SPI Serial Memory Interface Routines

Xicor offers a number of interesting and useful memory devices, including a line of serial EEPROMs and SerialFlash memories, which communicate using SPI-compatible signals. These routines implement a driver for the X25xxxx series of devices.

The functions allow you to read and write from the serial memory, using a bit-banged version of the interface. If you're using a microcontroller which implements SPI in hardware, you can easily modify the software to use that instead.

The functions let you read or write blocks of memory, or read 8, 16, or 32-bit variables. They also let you fiddle with the status register, and enable and disable the software-based data protection.

The code is provided in an archive (XICOR25.ZIP). that contains XICOR25.C and XICOR25.H. To use the module in your program, simply #include XICOR25.H from wherever you need to call the functions, and link XICOR25.C into your program.

This code was tested using an X25F032, using the PC parallel port for the interface, and using Borland's C/C++ v3.1 compiler, though it can easily be adapted for other targets and other compilers.


PC Printer Port I/O Module

While not strictly an embedded application, the standard PC printer port is handy for testing and controlling devices. It provides an easy way to implement a small amout of digital I/O. I like to use to during initial development of a product -- before the "real" hardware is ready, I can dummy up a circuit using the printer port, and thus get started testing my software.

This module provides the low-level control of the port, implementing code to control 12 outputs and read 5 inputs. This code was written for Borland C/C++ v3.1, but can be easily adapted for other compilers.

The code is provided in an archive (PRN_IO.ZIP). that contains PRN_IO.C and PRN_IO.H. To use the module in your program, simply #include PRN_IO.H from wherever you need to call the functions, and link PRN_IO.C into your program.

If you need more information on printer ports in general, the best to start looking is Fil's FAQ-Link-In Corner, which has an excellent collection of FAQs and related information.


Disclaimer
There is no warranty, either expressed or implied, with respect to the code contained on this page, it's quality, performance, or fitness for any particular purpose. All code is offered "as is". I am not responsible for what you do with the code (or what the code does to you). In other words, you're on your own ...

Also, I make no claims that this is great code. There are nearly always better and/or different ways to do things. These routines work for me, but your mileage may vary ...


This page is Copyright © 1997 by Randy Rasa.
Last updated 09-20-97.

width=88 ALT="Click Here!" border=0>

ODY> 1