This code (for Keil C51) sends new stable data samples from the assembler subroutine as serial data to a data analysis and logging program running on a PC.


// mbus machine
// author: Timothy Fox
#include "pragmas.h"

#include <reg52.h>

#include "defines.h"
#include "phdata.h"
#include "ppi.h"
#include "uart8250.h"
#include "tci.h"

/*
	PUBLIC    TD, CD, RDA    // from ASMVARS.INC
TD             bit      P1.5     ; true data line
CD             bit      P1.6     ; complement data line
RDA            bit      P1.7     ; read data line
*/

sbit    RDA = P1 ^ 7;
sbit    CD  = P1 ^ 6;
sbit    TD  = P1 ^ 5;

#define MBUS_TDCD_MASK  0x60
#define MBUS_RDA_MASK   0x80
#define MBUS_DATA_ONE   0x20
#define MBUS_DATA_ZERO  0x40


#define RDA_in      PhBdata1_0
#define RDA_sent    PhBdata1_1

extern  bit   got_CR, S_waiting, MBsending, MBprequeue, RDA_in, RDA_sent, AltMode;
static  Byte  mbus_state, got_bits, sent_bits;
struct MBUSword
{
    Byte    lbyte;
    Byte    hbyte;
};

union 
{
    struct  MBUSword    MBW;
    Word    word;
}  txword, rxword;

#define mbsample   PhData1 

void    InitMBSampler(void);
void    Read_MBUS_state(void);

enum mbus_states 
{
    mbs_startup,
    mbs_pre_reset,
    mbs_reset,
    mbs_data,    
    mbs_idle
};

void    set_mbus_state(Byte next_state);

#pragma disable 
static 
void    set_mbus_state(Byte next_state)
{
    // set timer2 to 0000 (up-counter)
    TR2 = 0;
    TL2 = TH2 = 0;
    TR2 = 1;
    switch (next_state) 
    {
        case mbs_pre_reset :
            break;
        case mbs_reset :
            RD = 1;
            putc_8250('\n');
            start_8250_tx();
            break;
        case mbs_data :
            start_8250_tx();
            break;
        case mbs_idle :
            break;
    }    
    mbus_state = next_state;
}    

Byte    last_sample = 0;

sfr16   Timer2 = 0xcc;


unsigned int    Tmost, Tleast; // expect 120..150 uSec

void    mbus_proc(void)
// looks for exit conditions from the current state    
{   
    Word Tscan;

    TR2 = 0;
    TL2 = TH2 = 0;
    TR2 = 1;

    Read_MBUS_state(); // stores state in mbsample

    TR2 = 0;
    Tscan = Timer2;
    TR2 = 1;

    if (mbsample & 0x01)  // valid (stable) sample
    {
        if (mbsample != last_sample) 
        {
            last_sample = mbsample;
            putc_8250(last_sample);
            start_8250_tx();
            if ((mbsample & MBUS_TDCD_MASK) == 0) // new state is IDLE
            {
                AltMode ^= 1;
                SetAltLED(AltMode);
            }
        }
    }
    if (Tscan > Tmost)
        Tmost = Tscan; 

    if (Tscan) // must be > 0
    {
        if (Tscan $lt; Tleast)
    	    Tleast = Tscan;               
    }
}    


#pragma REGPARMS
void    init_mbusmon(void) 
{
    got_bits = sent_bits = 0;
    MBprequeue = 0;
    MBsending = 0;
    S_waiting = 0;
    purge_8250_rx(); // clears got_CR flag
    purge_8250_tx();
    InitMBSampler();
    // write '1's to all three MBUS lines
    RDA = 1;
    TD = 1;
    CD = 1;
    // set timer2 to free-run as a counter, do not generate an interrupt
    T2CON = 0;
    RCAP2L = RCAP2H = 0;
    Tmost = 0;
    Tleast = 0x1000; // expect 120..150 uSec
    set_mbus_state(mbs_pre_reset);
}