Skip to content

Commit bd4f97e

Browse files
committed
resolves #27 except for 300 baud
1 parent 7cde64a commit bd4f97e

File tree

2 files changed

+233
-19
lines changed

2 files changed

+233
-19
lines changed

cores/xmega/HardwareSerial.cpp

+231-18
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,233 @@ void serialEventRun(void)
10411041
//
10421042
// Note that THESE values assume F_CPU==32000000
10431043

1044-
uint16_t temp_get_baud(unsigned long baud, uint8_t use_u2x)
1044+
// baud <= F_CPU / 16 for 1x, F_CPU / 8 for 2x - above that gives you a value of '1'
1045+
//
1046+
// X = clk_2x ? 8 : 16 bscale >= 0: bsel = F_CPU / ( (2 ^ bscale) * X * baud) - 1
1047+
// baud = F_CPU / ( (2 ^ bscale) * X * (bsel + 1) )
1048+
// bscale < 0: bsel = (1 / (2 ^ (bscale))) * (F_CPU / (X * baud) - 1)
1049+
// baud = F_CPU / ( X * (((2 ^ bscale) * bsel) + 1) )
1050+
//
1051+
// NOTE: if bsel is zero for a given bscale, then use bscale=0 and bsel=2^(bscale - 1)
1052+
// see section 19.3.1
1053+
//
1054+
// find 'best fit baud' by calculating the best 'bscale' and 'bsel' for a given baud
1055+
// bscale is -7 through +7 so this can be done in a simple loop
1056+
//
1057+
// Note that I have managed to "nuke out" some accurate integer math to make this work
1058+
// although the converging solutions tend to take up some time. It's still fast, though
1059+
// and you won't be calling this very often, now will ya?
1060+
1061+
// calculating BSEL and BAUD correctly - this lets me select _ANY_ baud rate
1062+
1063+
1064+
#if 1 // use NEW get_baud - it's about 810 bytes bigger, though
1065+
1066+
// GetBSEL returns the BSEL value given the baud and BSCALE
1067+
// this is more of an estimate. to get the right answer, this is
1068+
// merely a starting point. you have to converge on the solution
1069+
// by using a loop and picking the best 'nearby' value, up to '4' away
1070+
static int GetBSEL(unsigned long lBaud, int nBSCALE, int b2X)
1071+
{
1072+
long l1, l3;
1073+
unsigned char nFactor;
1074+
1075+
1076+
if(b2X)
1077+
{
1078+
nFactor = 8;
1079+
}
1080+
else
1081+
{
1082+
nFactor = 16;
1083+
}
1084+
1085+
if(nBSCALE >= 0)
1086+
{
1087+
l1 = nFactor * lBaud;
1088+
1089+
if(nBSCALE)
1090+
{
1091+
l1 = l1 << nBSCALE;
1092+
}
1093+
1094+
if(!l1)
1095+
{
1096+
return 0;
1097+
}
1098+
1099+
if((((long)F_CPU) % l1) < (l1 >> 1))
1100+
{
1101+
l1 = (((long)F_CPU) / l1) - 1;
1102+
}
1103+
else
1104+
{
1105+
l1 = (((long)F_CPU) / l1); // rounded off
1106+
}
1107+
}
1108+
else // nBSCALE < 0
1109+
{
1110+
l1 = nFactor * lBaud;
1111+
1112+
l3 = F_CPU;
1113+
1114+
if(nBSCALE > -4) // might overload if I use 32-bit integers and 32Mhz
1115+
{
1116+
l3 = l3 << (-nBSCALE);
1117+
}
1118+
else
1119+
{
1120+
l3 = l3 << 3;
1121+
l1 = l1 >> -(3 + nBSCALE);
1122+
}
1123+
1124+
if(l3 % l1 < (l1 >> 1))
1125+
{
1126+
l1 = l3 / l1 - 1;
1127+
}
1128+
else
1129+
{
1130+
l1 = l3 / l1; // round up
1131+
}
1132+
}
1133+
1134+
return (int)l1;
1135+
}
1136+
1137+
// GetBAUD calculates the actual baud rate based on nBSCALE and nBSEL
1138+
// it is actually pretty accurate, matching what you seen in the manual
1139+
static long GetBAUD(int nBSCALE, int nBSEL, int b2X)
1140+
{
1141+
long l1, l3;
1142+
unsigned char nFactor;
1143+
#define GET_BAUD_SCALE_FACTOR 4096 /* scaling the math so I can improve accuracy */
1144+
1145+
if(b2X)
1146+
{
1147+
nFactor = 8;
1148+
}
1149+
else
1150+
{
1151+
nFactor = 16;
1152+
}
1153+
1154+
if(nBSCALE >= 0)
1155+
{
1156+
l1 = (long)nFactor * (nBSEL + 1); // nBSEL can be 1-4095; 16 * 4k is ~64k; then it gets shifted.
1157+
1158+
if(nBSCALE)
1159+
{
1160+
l1 = l1 << nBSCALE;
1161+
}
1162+
1163+
if(!l1)
1164+
{
1165+
return 0;
1166+
}
1167+
1168+
return ((long)F_CPU) / l1; // TODO: roundoff correction?
1169+
}
1170+
1171+
// nBSCALE < 0
1172+
1173+
l3 = (long)nFactor * GET_BAUD_SCALE_FACTOR; // scale factor improves precision
1174+
1175+
l1 = l3 * nBSEL;
1176+
1177+
if(nBSCALE)
1178+
{
1179+
l1 = l1 >> (-nBSCALE);
1180+
}
1181+
1182+
l1 += l3; // the '+ 1' multiplied by nFactor * GET_BAUD_SCALE_SCALE_FACTOR
1183+
1184+
if(!l1) // unlikely
1185+
{
1186+
return 0;
1187+
}
1188+
1189+
l3 = F_CPU % l1; // the remainder - this gives me better rounding with int math
1190+
1191+
return GET_BAUD_SCALE_FACTOR * (F_CPU / l1) // integer division, then mult by the scale
1192+
+ (GET_BAUD_SCALE_FACTOR * l3) / l1; // the fractional remainder [scaled]
1193+
}
1194+
1195+
// 'get_baud' - the official baud rate number thingy
1196+
// this returns (BSCALE << 12) | (BSEL & 0x3fff) for all practical purposes
1197+
uint16_t get_baud(unsigned long baud, uint8_t use_u2x)
1198+
{
1199+
int i1;
1200+
char i2;
1201+
char iBSCALE, iBSCALERange;
1202+
int iBSEL, iTemp;
1203+
int iMinErr, iErr;
1204+
1205+
1206+
1207+
// NOTE: 2^ABS(BSCALE) must at most be one half of the minimum number
1208+
// of clock cycles a frame requires
1209+
1210+
iBSCALERange = 7; // my initial maximum range
1211+
1212+
if(baud > (F_CPU / 1310720)) // so that the result fits in an integer
1213+
{
1214+
iTemp = (int)((F_CPU / 2) * 11L / baud); // half the # of clock cycles needed per 11-bits
1215+
1216+
while(iBSCALERange && (1 << iBSCALERange) >= iTemp)
1217+
{
1218+
iBSCALERange --;
1219+
}
1220+
}
1221+
1222+
iBSEL = 0; // initially zero for 'not found'
1223+
iBSCALE = 0;
1224+
iMinErr = 0x7fff; // grossly over expected value of error
1225+
1226+
for(i2=-iBSCALERange; i2 <= iBSCALERange; i2++)
1227+
{
1228+
iTemp = GetBSEL(baud, i2, use_u2x);
1229+
1230+
if(!iTemp || iTemp >= 2048) // out of range? - note actual max is 4095
1231+
{
1232+
continue; // don't even look at an invalid value
1233+
}
1234+
1235+
// derived experimentally, loop on range of iTemp - 4 to iTemp + 1
1236+
for(i1=iTemp > 4 ? iTemp - 4 : 0; i1 <= iTemp + 1; i1++)
1237+
{
1238+
iErr = (int)(GetBAUD(iBSCALE, i1, use_u2x) - baud); // my delta
1239+
1240+
if(iErr < 0) // smaller than call to 'abs()'
1241+
{
1242+
iErr = -iErr;
1243+
}
1244+
1245+
if(iErr < iMinErr)
1246+
{
1247+
// I shall keep the first one I find that is below the current min error
1248+
// and the first 'lowest' error is the one I return. This favors lower
1249+
// values of BSCALE which I understand helps the baud rate generator
1250+
// work better overall.
1251+
1252+
iBSEL = i1;
1253+
iBSCALE = i2;
1254+
iMinErr = iErr; // new error to stay below, now
1255+
}
1256+
}
1257+
}
1258+
1259+
if(!iBSEL)
1260+
{
1261+
return 1; // highest possible baud rate
1262+
}
1263+
1264+
return ((uint16_t)((int)iBSCALE << 12)) | (uint16_t)(iBSEL & 0x3fff);
1265+
}
1266+
1267+
#else // OLD get_baud
1268+
1269+
// the OLD version used baud rate values from a lookup table
1270+
uint16_t get_baud(unsigned long baud, uint8_t use_u2x)
10451271
{
10461272
uint16_t i1;
10471273
static const unsigned long aBaud[] PROGMEM = // standard baud rates
@@ -1104,25 +1330,10 @@ static const uint16_t a1x[] PROGMEM = // 1x constants for standard baud rates
11041330
}
11051331
}
11061332
1107-
// NOTE: baud <= F_CPU / 16 for 1x, F_CPU / 8 for 2x
1108-
//
1109-
// X = clk_2x ? 8 : 16 bscale >= 0: bsel = F_CPU / ( (2 ^ bscale) * X * baud) - 1
1110-
// baud = F_CPU / ( (2 ^ bscale) * X * (bsel + 1) )
1111-
// bscale < 0: bsel = (1 / (2 ^ (bscale))) * (F_CPU / (X * baud) - 1)
1112-
// baud = F_CPU / ( X * (((2 ^ bscale) * bsel) + 1) )
1113-
//
1114-
// NOTE: if bsel is zero for a given bscale, then use bscale=0 and bsel=2^(bscale - 1)
1115-
// see section 19.3.1
1116-
//
1117-
// find 'best fit baud' by calculating the best 'bscale' and 'bsel' for a given baud
1118-
// bscale is -7 through +7 so this can be done in a simple loop
1119-
// I determined that using floating point is almost MANDATORY to make this work. scaled ints
1120-
// would work too. But it's likely to take up WAY TOO MUCH SPACE in the NVRAM to be practical.
1121-
11221333
return 1; // for now [half the maximum baud rate]
11231334
}
11241335
1125-
1336+
#endif // 0,1
11261337

11271338

11281339
/////////////////////////////////////////////////////////////////////////////////////////////
@@ -1221,8 +1432,10 @@ void HardwareSerial::begin(unsigned long baud, byte config)
12211432
// baud rate calc - page 220 table 19-5 [for standard values]
12221433
// table 19-1 (page 211) for calculation formulae
12231434
// (also see theory discussion on page 219)
1224-
baud_setting = temp_get_baud(baud, use_u2x);
1435+
baud_setting = get_baud(baud, use_u2x);
12251436

1437+
// NOTE: I had some difficulty getting 300 baud to work. 600 baud worked ok though
1438+
// to get 300 baud to work, you might have to change things around a bit
12261439

12271440
oldSREG = SREG; // save old to restore interrupts as they were
12281441
cli(); // clear interrupt flag until I'm done assigning pin stuff

cores/xmega/HardwareSerial.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ class HardwareSerial : public Stream
106106
// NOTE: 'Serial1' will be the hardware serial and 'Serial' the USB serial
107107
// whenever 'USBCON' is defined in pins_arduino.h
108108

109-
#include "USBAPI.h"
109+
#include <USBAPI.h>
110+
110111
extern HardwareSerial Serial1;
111112
#else // normal hardware serial
112113
extern HardwareSerial Serial;

0 commit comments

Comments
 (0)