#!/usr/bin/perl

# Copyright (C) 2007 John C. Luciani Jr. 

# Calculates the MCTL value for the MSP430x1xx USART using the
# equations on pages 272-276 of SLAU049E.pdf 

use strict;
use warnings;
use Carp;

# test the example in SLAU049E.pdf

#&error_table(2400, 32768, 11, (1,1,0,1,0,1,1,0)); 

#&error_table(57600, 4000000, 11);                    

#&error_table(2400, 32768, 11);                    
&error_table(9600, 32768, 11);                    

#&error_table(38400, 1000000, 11);
#&error_table(115200, 1000000,11);

# error_table (1) computes values for the modulation bits and 
# (2) prints the transmit and recieve bit errors. 

# Default modulation bit values can be specified in @bits.
# $bits[0] = m0, $bits[1] = m1, ... $bits[7] = m7

sub error_table ($$$;@) {
    my ($br, $brclk, $num_bits, @bits) = @_;
    my @m; # modulation bits
    &calc_tx_error($br, $brclk, \@m, $num_bits, @bits);
    printf("\nBaud Rate = %i\n", $br);
    printf("BR Clock  = %i\n\n", $brclk);
    printf("MCTL      = 0x%X\n", &m_hex(\@m));
    printf("UxBR1     = 0x%02X\n", int($brclk/$br)>>8);
    printf("UxBR0     = 0x%02X\n\n", int($brclk/$br));
    printf("Transmit Errors\n\n",  );
    printf("      Min Error\n----- ------------\n");
    &print_error_table(\@m);
    printf("\nReceive Errors\n\n",  &m_hex(\@m));
    printf("      Min Error\n----- ------------\n");
    my $max_err;
    my $line_count = 0;
    foreach (0..$num_bits-1) {
	my $err = &rx_error($br, $brclk, $_, \@m);
	printf("m%-2i=%i err = 7.2f%%\n", $_, $m[$_]{bit}, $err);
	$max_err = $err unless defined $max_err && abs($max_err) > abs($err);
	printf("----- -------------\n") if ++$line_count % 4 == 0;
    }
    printf("      max = %7.2f%%\n", $max_err);
}

sub sum {
    my ($mref, $j0, $jn) = @_;
    my $sum = 0;
    map { $sum += $mref->[$_ % 8]{bit} } ($j0..$jn);
    return $sum;
}

# From SLAU049E.pdf page 274

sub rx_error {
    my ($br, $brclk, $j, $mref) = @_;
    my $uxbr = int($brclk/$br);
    my $err = (($br/$brclk) * (2 * ($mref->[0]{bit} + int($uxbr/2)) 
			       + ($j * $uxbr + &sum($mref, 1, $j)))
	       -1-$j) * 100;
}

# From SLAU049E.pdf page 273

sub tx_error {
    my ($br, $brclk, $j, $mref) = @_;
    my $uxbr = int($brclk/$br);
    my $err = (($br/$brclk) * (($j+1) * $uxbr + &sum($mref, 0, $j)) 
	       -($j + 1)) * 100;
    return($err);
}

sub m_hex {
    my $mref = shift;
    my $m = 0;
    my $bv = 1;
    foreach (0..7) {
	$m += $bv if $mref->[$_]{bit};
	$bv *= 2;
    }
    return($m);
}

sub print_error_table {
    my $mref = shift;
    my $max_err;
    my $line_count = 0;
    my $i = 0;
    foreach (@$mref) {
	my $err = $_->{bit} ? $_->{err1} : $_->{err0};
	printf("m%-2i=%i err = %7.2f%%   err0 = %7.2f%%  err1 = %7.2f%%\n", 
	       $i++,
	       $_->{bit}, 
	       $err, 
	       $_->{err0}, 
	       $_->{err1});
	$max_err = $err unless defined $max_err && abs($max_err) > abs($err);
	printf("----- -------------\n") if ++$line_count % 4 == 0;
    }
    printf("      max = %7.2f%%\n", $max_err);
}

sub calc_tx_error {
    my ($br, $brclk, $mref, $num_bits, @bits) = @_;
    foreach my $j (0..$num_bits - 1) {
	$mref->[$j]{bit} = 0;
	my $err0 = &tx_error($br, $brclk, $j, $mref);
	$mref->[$j]{bit} = 1;
	my $err1 = &tx_error($br, $brclk, $j, $mref);
	my $bit;

	# There are 8 modulation bits. If we are past the eighth bit
	# then wrap-around otherwise if a bit was passed in the
	# subroutine call then use it otherwise use the bit with the
	# lowest error.

	if ($j > 7) {
	    $bit = $mref->[$j % 8]{bit};
	} elsif (defined $bits[$j]) {
	    $bit = $bits[$j];
	} else {
	    $bit = abs($err0) < abs($err1) ? 0 : 1;
	}
	$mref->[$j] = { bit => $bit,
		        err0 => $err0,
		        err1 => $err1 }; 
    }
}


# Style (adapted from the Perl Cookbook, First Edition, Recipe 12.4)

# 1. Names of functions and local variables are all lowercase.
# 2. The program's persistent variables (either file lexicals
#    or package globals) are capitalized.
# 3. Identifiers with multiple words have each of these
#    separated by an underscore for readability.
# 4. Constants are all uppercase.
# 5. If the arrow operator (->) is followed by either a
#    method name or a variable containing a method name then
#    there is a space before and after the operator.


