SourceForge | Login | Login via SSL | New User via SSL | Disclaimer | Email Webmaster | HCS Home page |

Still under construction!

How to write software for a COMM-Link

Introduction

The primary purpose of these web articles is so a person with some knowledge of progamming (Under Windows, Unix or other operating system) can assist in the HCS project. In my previous article on Building a COMM-Link I discuss the hardware required for a simple COMM-Link. Here we'll go over what it takes to begin progamming the hardware.

One of the biggest differences between progamming on a PC with an operating system (such as Linux or Windows) and embedded programming is that you need to a lot more work before the program can do anything useful. You have to setup the stack on processors that support a stack (needed to pass variables and addresses for functions and interrupts). The second thing is there is no vast resource of RAM or hard disk storage. As a matter of fact many controllers have less than 1KB of RAM (depends on the controller you're using). If you're suing the Futurlec board we have a generous 64K of flash and 1K of RAM on board with a 32K RAM chip external (very generous by controller standards). Lastly you need to setup and monitor the registers properly to get things to work smoothly. If you attempt to output a character to the RS232 port before the last one is fully sent garbage will be sent out instead.

There is a lot of understanding needed to properly deal with embedded systems. While it is possible to purchase compilers which contain library functions that will hide much of the details from the programmer, we won't be using those in this article. There will be much more hands on; setting up the compiler, writting libraries, maybe some soldering and some simple interfacing to external devices (such as LEDS, switches and a PC).

Ok, so you've selected a board (and processor) now you want to write software for that processor to properly communicate on the RS485 network. Here is a list of the things you will need:

MCIR COMM-Link In the examples that follow I'll be using Futurelec P89C51 board for all our examples. I'll be using the SDCC C compiler for the 8051 microcontroller (my SDCC setup). At first we will use the RS232 port but later we'll switch over to the RS485 port. All the libraries I'll use along with any other file that are needed can be found here in my files directory.

Choosing the COMM-Links functionality

COMM-Links can handle Analog, Digital, or X10 signals for example. Really you are only limited by you imagination, the limits of the HCS II's protocols (speed is 1 issue) and the limited resources of the controller you've chosen. Our COMM-Link will be real simple. We're going hard code the COMM-Link to accept 4 digital inputs and 4 digial outputs. We'll be using LEDS (outputs) and simple push button switches (Inputs) We also need interrupts for sending and receiving characters (though we could use software polling also).

For initial testing of the software we'll use RS232 to the PC. Once we're satisfied that the code is properly receiving commands, handling the data and sending back the appropriate response we'll switch over to RS485. For those that don't have an HCS to work with I'll also provide pointers to an RS485 dongle and some PC software (source code available of course).

Language

Next you'll need to select a langauge to program in. There's C, BASIC, Assembly and other languages. I'll be using C for the examples on these pages and the SDCC compiler. I know C pretty well and the SDCC 8031/51 compiler work pretty well.

RS485 Networking

RS232 and RS485 are 2 different layer 1 protocols (electrical layer). RS232 is a protocol which allows for only 2 devices to be connected together. RS485 allows for many devices to be connected together. In half duplex basically only 1 device to transmit at a time. A higher layer protocol is required to set the rules for when a device is allowed to use the network.

The HCS II uses only 2 wires for it's RS485 network. It's communications are in half duplex (only 1 controller can send at a time) and the master controller (the HCS) decides who's turn it is to talk..

PC Hardware (CTS/RTS) - enabling transmit. The SC and it's controllers always have their receievers enabled at all times. The SC is the only device that can leave it's transmitter on. When a controller receieves a command with it's ID then it enables it's transmitter and can transmit it's message.

Software - taking turns

ASCII protocol

Interrupts

Inputs & Outputs

Writing the software

We'll start with the simplest of programs to compile the Hello World program. You can't get much simpler, right? On a PC you simply type in this:

#include <stdio.h>

main() {
    printf("Hello world\n");
}
  

Well I wish that were true. You see small embedded systems don't have an OS like your desktop PC has. So you need to write libraries (or use the libraries that come with your language) to initialize the peripherals. In this case we need access to a USART.

#include <stdio.h>
#include <8052.h>         // 8051 registers
#include <ser_ir.h>       // serial interrupt handler

typedef unsigned int  uint;
typedef unsigned char uchar;

#define XBUFLEN 10
#define RBUFLEN 10

static uchar rbuf[RBUFLEN], xbuf[XBUFLEN];
static uchar rcnt, xcnt, rpos, xpos;
static uchar busy;

/* This is a simple delay based on a busy loop    */
/* with a 22.1184 MHz crystal, the delay lasts    */
/* for approximately 1 ms times the number passed */
/* Of course, it will take longer if there are    */
/* interrupts occuring...                         */

void delay_ms(unsigned char ms) {
        ms;
        _asm
        mov     r0, dpl
00001$: mov     r1, #230
00002$: nop
        nop
        nop
        nop
        nop
        nop
        djnz    r1, 00002$
        djnz    r0, 00001$
        ret
        _endasm;
}

// delay ~ 10 sec

#define sec_10  100

void
delay() {
   int i;

   for(i = 0; i < sec_10; i++) {
       delay_ms(100);
   }
}

// ES   - IE.4 Serial port int enable
// SCON - [ FE/SM0 | SM1   | SM2 | REN | TB8 | RB8 | TI | RI  ]
// PCON - [ SMOD   | SMOD0 | -   | POF | GF1 | GF0 | PD | IDL ]
// TMOD - [ Gate   | C/-T  | M1  | M0 | Gate | C/-T | M1 | M0 ]
// TL1  - [ xxxx xxxx ]
// TH1  - [ xxxx xxxx ]
// TR0  - TCON.4 Timer 0 contol (Start = 1, Stop = 0)
// TR1  - TCON.6
// EA   - IE.7 Enable interrupts bit

// if CPU run @ /12 clock rate:
// 19200 if SMOD = 1,  9600 if SMOD = 0
// if CPU run @ /6  clock rate:
// 38400 if SMOD = 1, 19200 if SMOD = 0

void serial_init (void) {
    rcnt =  xcnt = rpos = xpos = 0;  /* init buffers */
    busy =  0;

    ES   =  0;

    SCON =  0x50;                // 8 bit, 1 stop, 1 start, REN enabled
    PCON |= 0x80;                // SMOD = 1 (SMOD1 set, double the baud rate, 1/32)

    TMOD &= 0x0f;                // use timer 1
    TMOD |= 0x20;

    TH1  =  0xFB;                // 19200bps with 18.432MHz crystal

    TR1  =  1;                   //

    ES   =  1;
    EA   =  1;                   // Enable all interrupts including ES (serial irq)
}

// Check both RI & TI, we can get both at the same time
// Receive before Transmit so we don't lose any input

void ser_handler (void) interrupt 4 {
    if (RI) {           // Receive interrupt
        RI = 0;
        /* don't overwrite chars already in buffer */
        if (rcnt < RBUFLEN) {
            rbuf [(rpos+rcnt++) % RBUFLEN] = SBUF;
        }
    }
    if (TI) {           // Transmit interrupt
        TI = 0;
        if (busy = xcnt) {   /* Assignment, _not_ comparison! */
            xcnt--;
            SBUF = xbuf [xpos++];
            if (xpos >= XBUFLEN) {
                xpos = 0;
            }
        }
    }
}

void ser_putc (unsigned char c) {
   while (xcnt >= XBUFLEN) /* wait for room in buffer */
           ;
   ES = 0;
   if (busy) {
           xbuf[(xpos+xcnt++) % XBUFLEN] = c;
   } else {
           SBUF = c;
           busy = 1;
   }
   ES = 1;
}

unsigned char ser_getc (void) {
    unsigned char c;
    while (!rcnt)   /* wait for character */
           ;
    ES = 0;
    rcnt--;
    c = rbuf [rpos++];
    if (rpos >= RBUFLEN)
        rpos = 0;
    ES = 1;
    return (c);
}
#pragma SAVE
#pragma NOINDUCTION

void ser_puts (unsigned char *s) {
    unsigned char c;
    while (c=*s++) {
        if (c == '\n') ser_putc ('\r');
        ser_putc (c);
    }
}

#pragma RESTORE

void ser_gets (unsigned char *s, unsigned char len) {
    unsigned char pos, c;

    pos = 0;
    while (pos <= len) {
        c = ser_getc ();
        if (c == '\r') continue;        /* discard CR's */
        s[pos++] = c;
        if (c == '\n') break;           /* NL terminates */
    }
    s[pos] = '\0';
}

unsigned char ser_can_xmt (void) {
    return XBUFLEN - xcnt;
}

unsigned char ser_can_rcv (void) {
    return rcnt;
}

void putchar(char c) {
    ser_putc(c);    // Let's use the Internal UART as default.
}

void main(void) {
    serial_init();              // Initial setup of the internal UART

    while(1) {
        printf_small("Hello World\r\n");
    
        delay();
    }
}
  

Getting the software onto the development board

All trademarks and copyrights on this page are properties of their respective owners. Forum comments are owned by the poster. The rest is copyright ©1999-2004 VA Linux Systems, Inc.

For further info email me at: ncherry@users.sourceforge.net.

Valid XHTML
    1.0!