Using NewSoftSerial with Wiblock Boards
The
NewSoftSerial
supports CPU frequencies of 8MHz, 16MHz
and 20MHz but not 12MHz which is the frequency that most wiblocks
boards operate at. The Perl script
soft-serial-calc
creates code that can be inserted into
NewSoftSerial
for 12MHz operation. By changing a constant at the top
of the script tables for other operating frequencies can be
generated.
To use the script copy the delay table generation code from
NewSoftSerial.cpp
into the
__DATA__
section of
soft-serial-calc
and run
the script. After making a backup of
NewSoftSerial.cpp
replace the delay table in the original file with the code that
was created by the script. Re-compile your program.
NB:This code has not been extensively tested. I did a quick
verification by using the script to generate a table for 20MHz using
the 8MHz and 16MHz table entries. I compared the 20MHz table
generated by the script to the existing 20MHz table. The values were
close to the old values. The lower baud rate values closer than the
higher baud rate values. Some tweeking may be required for higher baud
rates.
Delay Table
#if F_CPU == 16000000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 17, 17, 12, },
{ 57600, 10, 37, 37, 33, },
{ 38400, 25, 57, 57, 54, },
{ 31250, 31, 70, 70, 68, },
{ 28800, 34, 77, 77, 74, },
{ 19200, 54, 117, 117, 114, },
{ 14400, 74, 156, 156, 153, },
{ 9600, 114, 236, 236, 233, },
{ 4800, 233, 474, 474, 471, },
{ 2400, 471, 950, 950, 947, },
{ 1200, 947, 1902, 1902, 1899, },
{ 300, 3804, 7617, 7617, 7614, },
};
const int XMIT_START_ADJUSTMENT = 5;
#elif F_CPU == 8000000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 5, 5, 3, },
{ 57600, 1, 15, 15, 13, },
{ 38400, 2, 25, 26, 23, },
{ 31250, 7, 32, 33, 29, },
{ 28800, 11, 35, 35, 32, },
{ 19200, 20, 55, 55, 52, },
{ 14400, 30, 75, 75, 72, },
{ 9600, 50, 114, 114, 112, },
{ 4800, 110, 233, 233, 230, },
{ 2400, 229, 472, 472, 469, },
{ 1200, 467, 948, 948, 945, },
{ 300, 1895, 3805, 3805, 3802, },
};
const int XMIT_START_ADJUSTMENT = 4;
#elif F_CPU == 20000000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 3, 21, 21, 18, },
{ 57600, 20, 43, 43, 41, },
{ 38400, 37, 73, 73, 70, },
{ 31250, 45, 89, 89, 88, },
{ 28800, 46, 98, 98, 95, },
{ 19200, 71, 148, 148, 145, },
{ 14400, 96, 197, 197, 194, },
{ 9600, 146, 297, 297, 294, },
{ 4800, 296, 595, 595, 592, },
{ 2400, 592, 1189, 1189, 1186, },
{ 1200, 1187, 2379, 2379, 2376, },
{ 300, 4759, 9523, 9523, 9520, },
};
const int XMIT_START_ADJUSTMENT = 4;
#elif F_CPU == 12000000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 11, 11, 8, },
{ 57600, 6, 26, 26, 23, },
{ 38400, 14, 41, 42, 39, },
{ 31250, 19, 51, 52, 49, },
{ 28800, 23, 56, 56, 53, },
{ 19200, 37, 86, 86, 83, },
{ 14400, 52, 116, 116, 113, },
{ 9600, 82, 175, 175, 173, },
{ 4800, 172, 354, 354, 351, },
{ 2400, 350, 711, 711, 708, },
{ 1200, 707, 1425, 1425, 1422, },
{ 300, 2850, 5711, 5711, 5708, },
};
const int XMIT_START_ADJUSTMENT = 5;
#else
#error This version of NewSoftSerial does not support your clock clock frequency
#endif
soft-serial-calc
#!/usr/bin/perl
# Generates new delay tables for use in NewSoftSerial. New values are
# generated by calculating a point on the line that contains the 8MHz
# and 16MHz points.
# Copyright (C) 2010 John C. Luciani Jr.
# This program may be distributed or modified under the terms of
# version 0.2 of the No-Fee Software License published by
# John C. Luciani Jr.
# A copy of the license is at the end of this file.
use strict;
use warnings;
use Tie::IxHash;
use Carp;
use Data::Dumper;
# (x0,y0) 8MHz point
# (x1,y1) 16MHz point
# (x2,y2) calculated point
use constant X0 => 8000000; # Hz (point 0)
use constant X1 =>16000000; # Hz (point 1)
use constant X2 =>12000000; # Hz (new frequency)
#
# All delay values from the existing code block are read into the
# Timing hash. An indexed hash is used so that the code block can be
# recreated with the new values at the end.
#
# The hash is populated with the xmit_start_adjustment,
# and the delay values for rxcenter, rxintra, rxstop and tx.
#
# $Timing{CPU_FREQ}{adj} = ADJ_VALUE
# $Timing{CPU_FREQ}{delays}{BAUD} = { rxcenter => RXCENTER_VALUE,
# rxintra => RXINTRA_VALUE,
# rxstop => RXSTOP_VALUE,
# tx => TX_VALUE }
#
my @Fields = qw(rxcenter rxintra rxstop tx); # delay fields
my %Timing; # timing table
tie %Timing, "Tie::IxHash";
#
# Read the lines in between the __DATA__ and __END__ markers
# and populate %Timing
#
my $F_CPU; # Frequency contained in the #if F_CPU conditional
while (<DATA>) {
s/^\s*//; # Remove leading spaces
s/\s*$//; # Revove trailing spaces
next unless length; # Skip empty lines
last if /^__END__$/; # Skip lines after the end marker
if (s/\\\s*$//) { # Remove the continuation backslash and
$_ .= <DATA>; # append the next line to $_ then
redo unless eof; # restart the loop block after the conditional
}
$F_CPU = $1, next if /F_CPU\D+(\d+)/;
next unless defined $F_CPU;
#
# if the conditional block ends then the CPU frequency
# is undefined
#
$F_CPU = undef, next if /#else/;
$F_CPU = undef, next if /#elif/;
$F_CPU = undef, next if /#endif/;
#
# Save the data values
#
if (/XMIT_START_ADJUSTMENT\D+(\d+)/) {
$Timing{$F_CPU}{adj} = $1;
} elsif (/(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+/) {
my ($baud, @v) = ($1, $2, $3, $4, $5);
$Timing{$F_CPU}{delays}{$baud} = { map { $_ => shift(@v) } @Fields };
}
}
#
# foreach baud found generate a set of timing delays.
# timing delay.
#
my @bauds = reverse sort { $a <=> $b } keys % { $Timing{&X0}{delays} };
foreach my $baud (@bauds) {
$Timing{&X2}{delays}{$baud} = { map { $_ => &y2($baud,$_) } @Fields };
}
#
# generate the transmit start adjustment
#
my ($y1, $y0) = map { $Timing{$_}{adj} } (X1, X0);
$Timing{&X2}{adj} = &_y2(X0, $y0, X1, $y1, X2);
#
# Output the new table. New entries are at the bottom of the table
#
my @F_CPU = keys %Timing;
my $IF = "#if ";
while (@F_CPU) {
my $f_cpu = shift @F_CPU;
printf("%s F_CPU == %i\n\n", $IF, $f_cpu);
printf("static const DELAY_TABLE PROGMEM table[] = \n");
printf("{\n");
printf(" // baud rxcenter rxintra rxstop tx\n");
my @bauds = reverse sort { $a <=> $b } keys % { $Timing{$f_cpu}{delays} };
foreach my $baud (@bauds) {
my $timing = $Timing{$f_cpu}{delays}{$baud};
printf(" { %6i, %6i, %6i, %6i, %6i, },\n",
$baud, map { $timing->{$_} } @Fields);
}
printf("};\n\n");
printf("const int XMIT_START_ADJUSTMENT = %i;\n\n", $Timing{$f_cpu}{adj});
$IF = "#elif";
}
printf("#else\n\n");
printf("#error This version of NewSoftSerial does not support your clock clock frequency\n\n");
printf("#endif\n");
#
# calculates a new y value given (x0, y0), (x1, y1) and a
# new x value (x2)
#
sub _y2 {
my ($x0, $y0, $x1, $y1, $x2) = @_;
my $y2 = ($x2 - $x0) * ($y1 - $y0) / ($x1 - $x0) + $y0;
return(sprintf("%i", $y2 + 0.5));
}
#
# calculates a new y value given the baud rate and field
# X0, X1, X2 are global constants
#
sub y2 {
my ($baud, $field) = @_;
my ($y1, $y0) = map { $Timing{$_}{delays}{$baud}{$field} } (X1, X0);
return(&_y2(X0, $y0, X1, $y1, X2));
}
### Place the delay_table block from the original NewSoftSerial.cpp
### between the __DATA__ and __END__ markers.
__DATA__
#if F_CPU == 16000000
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 17, 17, 12, },
{ 57600, 10, 37, 37, 33, },
{ 38400, 25, 57, 57, 54, },
{ 31250, 31, 70, 70, 68, },
{ 28800, 34, 77, 77, 74, },
{ 19200, 54, 117, 117, 114, },
{ 14400, 74, 156, 156, 153, },
{ 9600, 114, 236, 236, 233, },
{ 4800, 233, 474, 474, 471, },
{ 2400, 471, 950, 950, 947, },
{ 1200, 947, 1902, 1902, 1899, },
{ 300, 3804, 7617, 7617, 7614, },
};
const int XMIT_START_ADJUSTMENT = 5;
#elif F_CPU == 8000000
static const DELAY_TABLE table[] PROGMEM =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 1, 5, 5, 3, },
{ 57600, 1, 15, 15, 13, },
{ 38400, 2, 25, 26, 23, },
{ 31250, 7, 32, 33, 29, },
{ 28800, 11, 35, 35, 32, },
{ 19200, 20, 55, 55, 52, },
{ 14400, 30, 75, 75, 72, },
{ 9600, 50, 114, 114, 112, },
{ 4800, 110, 233, 233, 230, },
{ 2400, 229, 472, 472, 469, },
{ 1200, 467, 948, 948, 945, },
{ 300, 1895, 3805, 3805, 3802, },
};
const int XMIT_START_ADJUSTMENT = 4;
#elif F_CPU == 20000000
// 20MHz support courtesy of the good people at macegr.com.
// Thanks, Garrett!
static const DELAY_TABLE PROGMEM table[] =
{
// baud rxcenter rxintra rxstop tx
{ 115200, 3, 21, 21, 18, },
{ 57600, 20, 43, 43, 41, },
{ 38400, 37, 73, 73, 70, },
{ 31250, 45, 89, 89, 88, },
{ 28800, 46, 98, 98, 95, },
{ 19200, 71, 148, 148, 145, },
{ 14400, 96, 197, 197, 194, },
{ 9600, 146, 297, 297, 294, },
{ 4800, 296, 595, 595, 592, },
{ 2400, 592, 1189, 1189, 1186, },
{ 1200, 1187, 2379, 2379, 2376, },
{ 300, 4759, 9523, 9523, 9520, },
};
const int XMIT_START_ADJUSTMENT = 4;
#endif
__END__
# 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.
##### No-Fee Software License Version 0.2
#### Intent
### The intent of this license is to allow for distribution of this
### software without fee. Usage of this software other than
### distribution, is unrestricted.
#### License
### Permission is granted to make and distribute verbatim copies of
### this software provided that (1) no fee is charged and (2) the
### original copyright notice and this license document are preserved
### on all copies.
### Permission is granted to make and distribute modified versions of
### this software under the conditions for verbatim copying provided
### that the entire resulting derived work is distributed under terms
### of a license identical to this one.
### This software is provided by the author "AS IS" and any express or
### implied warranties, including, but not limited to, the implied
### warranties of merchantability and fitness for a particular purpose
### are disclaimed. In no event shall the author be liable for any
### direct, indirect, incidental, special, exemplary, or consequential
### damages (including, but not limited to, procurement of substitute
### goods or services; loss of use, data, or profits; or business
### interruption) however caused and on any theory of liability,
### whether in contract, strict liability, or tort (including
### negligence or otherwise) arising in any way out of the use of this
### software, even if advised of the possibility of such damage.