Skip to content

Commit ee9f2a3

Browse files
author
Nathan Seidle
committed
Adding in TX portion. Works well at 115200.
1 parent 293bed9 commit ee9f2a3

File tree

2 files changed

+222
-23
lines changed

2 files changed

+222
-23
lines changed

libraries/SoftwareSerial/src/SoftwareSerial.cpp

Lines changed: 207 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
SoftwareSerial *ap3_active_softwareserial_handle = 0;
3939

4040
//Uncomment to enable debug pulses and Serial.prints
41-
//#define DEBUG
41+
#define DEBUG
4242

4343
#ifdef DEBUG
4444
#define SS_DEBUG_PIN 9
@@ -105,14 +105,15 @@ bool SoftwareSerial::isListening()
105105

106106
void SoftwareSerial::begin(uint32_t baudRate, HardwareSerial_Config_e SSconfig)
107107
{
108-
pinMode(_txPin, OUTPUT);
109108
digitalWrite(_txPin, _invertLogic ? LOW : HIGH);
109+
pinMode(_txPin, OUTPUT);
110110

111111
pinMode(_rxPin, INPUT);
112112
if (_invertLogic == false)
113113
pinMode(_rxPin, INPUT_PULLUP); //Enable external pullup if using normal logic
114114

115115
#ifdef DEBUG
116+
am_hal_gpio_output_clear(debugPad);
116117
pinMode(SS_DEBUG_PIN, OUTPUT);
117118
#endif
118119

@@ -126,13 +127,18 @@ void SoftwareSerial::begin(uint32_t baudRate, HardwareSerial_Config_e SSconfig)
126127
//Set variables data bits, stop bits and parity based on config
127128
softwareserialSetConfig(SSconfig);
128129

129-
sysTicksPerBit = (TIMER_FREQ / baudRate) * 0.98; //Short the number of sysTicks a small amount because we are doing a mod operation
130+
rxSysTicksPerBit = (TIMER_FREQ / baudRate) * 0.98; //Shorten the number of sysTicks a small amount because we are doing a mod operation
131+
txSysTicksPerBit = (TIMER_FREQ / baudRate) - 6; //Shorten the txSysTicksPerBit by the number of ticks needed to run the txHandler ISR
130132

131-
sysTicksPerByte = (TIMER_FREQ / baudRate) * (_dataBits + _parityBits + _stopBits);
133+
rxSysTicksPerByte = (TIMER_FREQ / baudRate) * (_dataBits + _parityBits + _stopBits);
132134

133135
//During RX, if leftover systicks is more than a fraction of a bit, we will call it a bit
134136
//This is needed during 115200 when cmpr ISR extends into the start bit of the following byte
135-
sysTicksPartialBit = sysTicksPerBit / 4;
137+
rxSysTicksPartialBit = rxSysTicksPerBit / 4;
138+
139+
txSysTicksPerStopBit = txSysTicksPerBit * _stopBits;
140+
141+
Serial.printf("sysTicksPerBit: %d\n", txSysTicksPerBit);
136142

137143
//Clear pin change interrupt
138144
am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(_rxPad));
@@ -179,14 +185,99 @@ int SoftwareSerial::peek()
179185
//Clears flag when called
180186
bool SoftwareSerial::overflow()
181187
{
182-
if (_rxBufferOverflow)
188+
if (_rxBufferOverflow || _txBufferOverflow)
183189
{
184190
_rxBufferOverflow = false;
191+
_txBufferOverflow = false;
185192
return (true);
186193
}
187194
return (false);
188195
}
189196

197+
void SoftwareSerial::write(uint8_t toSend)
198+
{
199+
//See if we are going to overflow buffer
200+
uint8_t nextSpot = (txBufferHead + 1) % AP3_SS_BUFFER_SIZE;
201+
if (nextSpot != txBufferTail)
202+
{
203+
//Add this byte into the circular buffer
204+
txBuffer[nextSpot] = toSend;
205+
txBufferHead = nextSpot;
206+
}
207+
else
208+
{
209+
_txBufferOverflow = true;
210+
}
211+
212+
//See if hardware is available
213+
if (txInUse == false)
214+
{
215+
txInUse = true;
216+
217+
//Start sending this byte immediately
218+
txBufferTail = (txBufferTail + 1) % AP3_SS_BUFFER_SIZE;
219+
outgoingByte = txBuffer[txBufferTail];
220+
221+
//Calc parity
222+
calcParityBit();
223+
224+
beginTX();
225+
}
226+
}
227+
228+
//Starts the transmission of the next available byte from the buffer
229+
void SoftwareSerial::beginTX()
230+
{
231+
bitCounter = 0;
232+
233+
am_hal_gpio_output_set(debugPad);
234+
235+
//Initiate start bit
236+
if (_invertLogic == false)
237+
{
238+
am_hal_gpio_output_clear(_txPad); //Normal logic, low is start bit
239+
}
240+
else
241+
{
242+
am_hal_gpio_output_set(_txPad);
243+
}
244+
245+
//Setup ISR to trigger when we are in middle of start bit
246+
//am_hal_stimer_compare_delta_set(7, txSsysTicksPerBit);
247+
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = txSysTicksPerBit; //Direct reg write to decrease execution time
248+
249+
// Enable the timer interrupt in the NVIC.
250+
NVIC_EnableIRQ(STIMER_CMPR7_IRQn);
251+
252+
am_hal_gpio_output_clear(debugPad);
253+
}
254+
255+
//Assumes the global variables have been set: _parity, _dataBits, outgoingByte
256+
//Sets global variable _parityBit
257+
void SoftwareSerial::calcParityBit()
258+
{
259+
if (_parity == 0)
260+
return; //No parity
261+
262+
uint8_t ones = 0;
263+
for (uint8_t x = 0; x < _dataBits; x++)
264+
{
265+
if (outgoingByte & (0x01 << x))
266+
{
267+
ones++;
268+
}
269+
}
270+
271+
if (_parity == 1) //Odd
272+
{
273+
_parityForByte = !(ones % 2);
274+
}
275+
else //Even
276+
{
277+
_parityForByte = (ones % 2);
278+
}
279+
}
280+
190281
ap3_err_t SoftwareSerial::softwareserialSetConfig(HardwareSerial_Config_e SSconfig)
191282
{
192283
ap3_err_t retval = AP3_OK;
@@ -338,29 +429,28 @@ void SoftwareSerial::rxBit(void)
338429
bitCounter = 0;
339430
lastBitTime = bitTime;
340431
bitType = false;
432+
rxInUse = true; //Indicate we are now in process of receiving a byte
341433

342434
//Setup cmpr7 interrupt to handle overall timeout
343-
//am_hal_stimer_compare_delta_set(7, sysTicksPerByte);
344-
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = sysTicksPerByte; //Direct reg write to decrease execution time
435+
//am_hal_stimer_compare_delta_set(7, rxSysTicksPerByte);
436+
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = rxSysTicksPerByte; //Direct reg write to decrease execution time
345437

346438
// Enable the timer interrupt in the NVIC.
347439
NVIC_EnableIRQ(STIMER_CMPR7_IRQn);
348440
}
349441
else
350442
{
351443
//Calculate the number of bits that have occured since last PCI
352-
//Then add those bits of the current bitType (either 1 or 0) to
353-
//the byte
354-
uint8_t numberOfBits = (bitTime - lastBitTime) / sysTicksPerBit;
444+
uint8_t numberOfBits = (bitTime - lastBitTime) / rxSysTicksPerBit;
355445

356446
if (bitCounter == 0)
357447
{
358448
//Catch any partial bits
359449
//For very high bauds (115200) the final interrupt spills over into the
360450
//start bit of the next byte. This catches the partial systicks and correctly
361451
//identifies the start bit as such.
362-
uint16_t partialBits = (bitTime - lastBitTime) % sysTicksPerBit;
363-
if (partialBits > sysTicksPartialBit)
452+
uint16_t partialBits = (bitTime - lastBitTime) % rxSysTicksPerBit;
453+
if (partialBits > rxSysTicksPartialBit)
364454
{
365455
#ifdef DEBUG
366456
Serial.println("Partial!");
@@ -384,7 +474,7 @@ void SoftwareSerial::rxBit(void)
384474
}
385475
}
386476

387-
for (uint8_t y = 0; y < numberOfBits; y++) //Number of bits in this chunk of time
477+
for (uint8_t y = 0; y < numberOfBits; y++) //Add bits of the current bitType (either 1 or 0) to our byte
388478
{
389479
incomingByte >>= 1;
390480
if (bitType == true)
@@ -400,7 +490,7 @@ void SoftwareSerial::rxBit(void)
400490
#endif
401491
}
402492

403-
void SoftwareSerial::endOfByte()
493+
void SoftwareSerial::rxEndOfByte()
404494
{
405495
//Finish out bytes that are less than 8 bits
406496
#ifdef DEBUG
@@ -459,12 +549,105 @@ void SoftwareSerial::endOfByte()
459549

460550
lastBitTime = 0; //Reset for next byte
461551

462-
rxInUse = false;
552+
rxInUse = false; //Release so that we can TX if needed
463553

464554
// Disable the timer interrupt in the NVIC.
465555
NVIC_DisableIRQ(STIMER_CMPR7_IRQn);
466556
}
467557

558+
//Called from cmprX ISR
559+
//Sends out a bit with each cmprX ISR trigger
560+
void SoftwareSerial::txHandler()
561+
{
562+
if (bitCounter < _dataBits) //Data bits 0 to 7
563+
{
564+
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = txSysTicksPerBit; //Direct reg write to decrease execution time
565+
if (outgoingByte & 0x01)
566+
{
567+
am_hal_gpio_output_set(_txPad);
568+
}
569+
else
570+
{
571+
am_hal_gpio_output_clear(_txPad);
572+
}
573+
outgoingByte >>= 1;
574+
bitCounter++;
575+
}
576+
else if (bitCounter == _dataBits) //Send parity bit or stop bit(s)
577+
{
578+
if (_parity)
579+
{
580+
//Send parity bit
581+
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = txSysTicksPerBit; //Direct reg write to decrease execution time
582+
if (_parityForByte)
583+
{
584+
am_hal_gpio_output_set(_txPad);
585+
}
586+
else
587+
{
588+
am_hal_gpio_output_clear(_txPad);
589+
}
590+
}
591+
else
592+
{
593+
//Send stop bit
594+
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = txSysTicksPerStopBit; //Direct reg write to decrease execution time
595+
am_hal_gpio_output_set(_txPad);
596+
}
597+
bitCounter++;
598+
}
599+
else if (bitCounter == (_dataBits + 1)) //Send stop bit or begin next byte
600+
{
601+
if (_parity)
602+
{
603+
//Send stop bit
604+
AM_REGVAL(AM_REG_STIMER_COMPARE(0, 7)) = txSysTicksPerStopBit; //Direct reg write to decrease execution time
605+
am_hal_gpio_output_set(_txPad);
606+
bitCounter++;
607+
}
608+
else
609+
{
610+
//Start next byte
611+
if (txBufferTail == txBufferHead)
612+
{
613+
// Disable the timer interrupt in the NVIC.
614+
NVIC_DisableIRQ(STIMER_CMPR7_IRQn);
615+
616+
//All done!
617+
txInUse = false;
618+
}
619+
else
620+
{
621+
//Send next byte in buffer
622+
txBufferTail = (txBufferTail + 1) % AP3_SS_BUFFER_SIZE;
623+
outgoingByte = txBuffer[txBufferTail];
624+
calcParityBit();
625+
beginTX();
626+
}
627+
}
628+
}
629+
else if (bitCounter == (_dataBits + 2)) //Begin next byte
630+
{
631+
//Start next byte
632+
if (txBufferTail == txBufferHead)
633+
{
634+
// Disable the timer interrupt in the NVIC.
635+
NVIC_DisableIRQ(STIMER_CMPR7_IRQn);
636+
637+
//All done!
638+
txInUse = false;
639+
}
640+
else
641+
{
642+
//Send next byte in buffer
643+
txBufferTail = (txBufferTail + 1) % AP3_SS_BUFFER_SIZE;
644+
outgoingByte = txBuffer[txBufferTail];
645+
calcParityBit();
646+
beginTX();
647+
}
648+
}
649+
}
650+
468651
//Called at the completion of bytes
469652
extern "C" void am_stimer_cmpr7_isr(void)
470653
{
@@ -477,7 +660,14 @@ extern "C" void am_stimer_cmpr7_isr(void)
477660
{
478661
am_hal_stimer_int_clear(AM_HAL_STIMER_INT_COMPAREH);
479662

480-
ap3_active_softwareserial_handle->endOfByte();
663+
if (ap3_active_softwareserial_handle->rxInUse == true)
664+
{
665+
ap3_active_softwareserial_handle->rxEndOfByte();
666+
}
667+
else if (ap3_active_softwareserial_handle->txInUse == true)
668+
{
669+
ap3_active_softwareserial_handle->txHandler();
670+
}
481671
}
482672

483673
#ifdef DEBUG

libraries/SoftwareSerial/src/SoftwareSerial.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,22 @@ class SoftwareSerial
6161
bool overflow();
6262

6363
void rxBit(void);
64-
void endOfByte(void);
64+
void rxEndOfByte(void);
65+
66+
void write(uint8_t toSend);
67+
void beginTX();
68+
void calcParityBit();
69+
void txHandler(void);
70+
71+
volatile bool rxInUse = false;
72+
volatile bool txInUse = false;
6573

6674
private:
6775
void startRXListening(void);
6876

6977
uint8_t txBuffer[AP3_SS_BUFFER_SIZE];
7078
uint16_t txBufferHead = 0;
7179
volatile uint16_t txBufferTail = 0;
72-
volatile bool txInUse = false;
7380
volatile uint8_t outgoingByte = 0;
7481

7582
volatile uint8_t rxBuffer[AP3_SS_BUFFER_SIZE];
@@ -92,18 +99,20 @@ class SoftwareSerial
9299
bool _invertLogic;
93100

94101
//For RX
95-
uint16_t sysTicksPerBit = 0;
96-
uint32_t sysTicksPerByte = 0;
97-
uint16_t sysTicksPartialBit = 0;
102+
uint16_t rxSysTicksPerBit = 0;
103+
uint32_t rxSysTicksPerByte = 0;
104+
uint16_t rxSysTicksPartialBit = 0;
98105
volatile uint8_t numberOfBits[10];
99106
volatile uint32_t lastBitTime = 0;
100107
volatile uint8_t bitCounter;
101108
volatile bool bitType = false;
102-
volatile bool rxInUse = false;
103109
bool _rxBufferOverflow = false;
104110

105111
//For TX
112+
uint16_t txSysTicksPerBit = 0;
113+
uint16_t txSysTicksPerStopBit = 0;
106114
uint8_t _parityForByte = 0; //Calculated per byte
115+
bool _txBufferOverflow = false;
107116
};
108117

109118
#endif

0 commit comments

Comments
 (0)