@@ -101,24 +101,38 @@ void UartClass::_tx_data_empty_irq(void)
101
101
// buffer. Send the next byte
102
102
unsigned char c = _tx_buffer[_tx_buffer_tail];
103
103
_tx_buffer_tail = (_tx_buffer_tail + 1 ) % SERIAL_TX_BUFFER_SIZE;
104
-
105
- (*_hwserial_module).TXDATAL = c;
106
-
104
+
107
105
// clear the TXCIF flag -- "can be cleared by writing a one to its bit
108
106
// location". This makes sure flush() won't return until the bytes
109
107
// actually got written
110
- (*_hwserial_module).STATUS |= USART_TXCIF_bm;
111
-
112
- if (_tx_buffer_head == _tx_buffer_tail) {
113
- // Buffer empty, so disable "data register empty" interrupt
114
- (*_hwserial_module).CTRLA &= (~USART_DREIE_bm);
115
- }
108
+ (*_hwserial_module).STATUS = USART_TXCIF_bm;
109
+
110
+ (*_hwserial_module).TXDATAL = c;
111
+
112
+ while (!((*_hwserial_module).STATUS & USART_DREIF_bm));
113
+
114
+ if (_tx_buffer_head == _tx_buffer_tail) {
115
+ // Buffer empty, so disable "data register empty" interrupt
116
+ (*_hwserial_module).CTRLA &= (~USART_DREIE_bm);
117
+
118
+ // Take the DRE interrupt back no normal priority level if it has been elevated
119
+ if (_hwserial_dre_interrupt_elevated){
120
+ CPUINT.LVL1VEC = _prev_lvl1_interrupt_vect;
121
+ _hwserial_dre_interrupt_elevated = 0 ;
122
+ }
123
+ }
116
124
}
117
125
118
126
// Public Methods //////////////////////////////////////////////////////////////
119
127
120
128
void UartClass::begin (unsigned long baud, uint16_t config)
121
129
{
130
+ // Make sure no transmissions are ongoing and USART is disabled in case begin() is called by accident
131
+ // without first calling end()
132
+ if (_written){
133
+ this ->end ();
134
+ }
135
+
122
136
// uint16_t baud_setting = 0;
123
137
int32_t baud_setting = 0 ;
124
138
uint8_t error = 0 ;
@@ -127,16 +141,6 @@ void UartClass::begin(unsigned long baud, uint16_t config)
127
141
uint8_t oldSREG = SREG;
128
142
cli ();
129
143
130
- // Set up the rx and rx pins
131
- pinMode (_hwserial_rx_pin, INPUT_PULLUP);
132
-
133
- pinMode (_hwserial_tx_pin, OUTPUT);
134
- digitalWrite (_hwserial_tx_pin, HIGH);
135
-
136
- // Make sure no transmissions are ongoing in case begin() is called by accident
137
- // without first calling end()
138
- flush ();
139
-
140
144
// ********Check if desired baud rate is within the acceptable range for using CLK2X RX-mode********
141
145
// Condition from datasheet
142
146
// This limits the minimum baud_setting value to 64 (0x0040)
@@ -178,7 +182,7 @@ void UartClass::begin(unsigned long baud, uint16_t config)
178
182
error = 1 ;
179
183
}
180
184
181
- // Do nothing if an invalid baud rate is requested
185
+ // Do nothing if an invalid baud rate is requested
182
186
if (!error) {
183
187
184
188
#ifdef PERFORM_BAUD_CORRECTION
@@ -199,15 +203,25 @@ void UartClass::begin(unsigned long baud, uint16_t config)
199
203
}
200
204
#endif
201
205
202
- // assign the baud_setting, a.k.a. BAUD (USART Baud Rate Register)
203
- (*_hwserial_module).BAUD = (int16_t ) baud_setting;
204
-
205
206
_written = false ;
206
207
207
- (*_hwserial_module).CTRLC = config;
208
+ // Set up the rx pin
209
+ pinMode (_hwserial_rx_pin, INPUT_PULLUP);
210
+
211
+ // Set up the tx pin
212
+ digitalWrite (_hwserial_tx_pin, HIGH);
213
+ pinMode (_hwserial_tx_pin, OUTPUT);
208
214
215
+ // assign the baud_setting, a.k.a. BAUD (USART Baud Rate Register)
216
+ (*_hwserial_module).BAUD = (int16_t ) baud_setting;
217
+
218
+ // Set USART mode of operation
219
+ (*_hwserial_module).CTRLC = config;
220
+
221
+ // Enable transmitter and receiver
222
+ (*_hwserial_module).CTRLB |= (USART_RXEN_bm | USART_TXEN_bm);
223
+
209
224
(*_hwserial_module).CTRLA |= USART_RXCIE_bm;
210
- (*_hwserial_module).CTRLB |= (USART_RXEN_bm | USART_TXEN_bm);
211
225
}
212
226
213
227
// Restore SREG content
@@ -277,16 +291,25 @@ void UartClass::flush()
277
291
if (!_written) {
278
292
return ;
279
293
}
294
+
295
+ // Check if we are inside an ISR already (e.g. connected to a different peripheral then UART), in which case the UART ISRs will not be called.
296
+ // Temporarily elevate the DRE interrupt to allow it to run.
297
+ if (CPUINT.STATUS & CPUINT_LVL0EX_bm){
298
+ // Elevate the priority level of the Data Register Empty Interrupt vector
299
+ // and copy whatever vector number that might be in the register already.
300
+ _prev_lvl1_interrupt_vect = CPUINT.LVL1VEC ;
301
+ CPUINT.LVL1VEC = _hwserial_dre_interrupt_vect_num;
302
+
303
+ _hwserial_dre_interrupt_elevated = 1 ;
304
+ }
280
305
281
306
// Spin until the data-register-empty-interrupt is disabled and TX complete interrupt flag is raised
282
307
while ( ((*_hwserial_module).CTRLA & USART_DREIE_bm) || (!((*_hwserial_module).STATUS & USART_TXCIF_bm)) ) {
283
308
284
309
// If interrupts are globally disabled or the and DR empty interrupt is disabled,
285
310
// poll the "data register empty" interrupt flag to prevent deadlock
286
- if ( (!(SREG & CPU_I_bm)) || (!((*_hwserial_module).CTRLA & USART_DREIE_bm)) ){ // TODO: Verify that || instead of && is correct here
287
- if ( (*_hwserial_module).STATUS & USART_DREIF_bm ){
288
- _tx_data_empty_irq ();
289
- }
311
+ if ( (!(SREG & CPU_I_bm)) || (!((*_hwserial_module).CTRLA & USART_DREIE_bm)) ){
312
+ _tx_data_empty_irq ();
290
313
}
291
314
}
292
315
// If we get here, nothing is queued anymore (DREIE is disabled) and
@@ -303,7 +326,7 @@ size_t UartClass::write(uint8_t c)
303
326
// 500kbit/s) bit rates, where interrupt overhead becomes a slowdown.
304
327
if ( (_tx_buffer_head == _tx_buffer_tail) && ((*_hwserial_module).STATUS & USART_DREIF_bm) ) {
305
328
(*_hwserial_module).TXDATAL = c;
306
- (*_hwserial_module).STATUS | = USART_TXCIF_bm;
329
+ (*_hwserial_module).STATUS = USART_TXCIF_bm;
307
330
308
331
// Make sure data register empty interrupt is disabled to avoid
309
332
// that the interrupt handler is called in this situation
@@ -312,24 +335,33 @@ size_t UartClass::write(uint8_t c)
312
335
return 1 ;
313
336
}
314
337
338
+ // Check if we are inside an ISR already (could be from by a source other than UART),
339
+ // in which case the UART ISRs will be blocked.
340
+ if (CPUINT.STATUS & CPUINT_LVL0EX_bm){
341
+ // Elevate the priority level of the Data Register Empty Interrupt vector
342
+ // and copy whatever vector number that might be in the register already.
343
+ _prev_lvl1_interrupt_vect = CPUINT.LVL1VEC ;
344
+ CPUINT.LVL1VEC = _hwserial_dre_interrupt_vect_num;
345
+
346
+ _hwserial_dre_interrupt_elevated = 1 ;
347
+ }
348
+
315
349
tx_buffer_index_t i = (_tx_buffer_head + 1 ) % SERIAL_TX_BUFFER_SIZE;
316
350
317
- // If the output buffer is full, there's nothing for it other than to
318
- // wait for the interrupt handler to empty it a bit
319
- while (i == _tx_buffer_tail) {
320
-
321
- if ( (!(SREG & CPU_I_bm)) || (!((*_hwserial_module).CTRLA & USART_DREIE_bm)) ) {// TODO: Verify addition of DREIE-check
322
- // Interrupts are disabled either globally or for data register empty,
323
- // so we'll have to poll the "data register empty" flag ourselves.
324
- // If it is set, pretend an interrupt has happened and call the handler
325
- // to free up space for us.
326
- if ( (*_hwserial_module).STATUS & USART_DREIF_bm) {
327
- _tx_data_empty_irq ();
328
- }
329
- } else {
330
- // nop, the interrupt handler will free up space for us
331
- }
332
- }
351
+ // If the output buffer is full, there's nothing for it other than to
352
+ // wait for the interrupt handler to empty it a bit
353
+ while (i == _tx_buffer_tail) {
354
+ if ( ( !(SREG & CPU_I_bm) ) || ( !((*_hwserial_module).CTRLA & USART_DREIE_bm) ) ){
355
+ // Interrupts are disabled either globally or for data register empty,
356
+ // so we'll have to poll the "data register empty" flag ourselves.
357
+ // If it is set, pretend an interrupt has happened and call the handler
358
+ // to free up space for us.
359
+
360
+ _tx_data_empty_irq ();
361
+ } else {
362
+ // nop, the interrupt handler will free up space for us
363
+ }
364
+ }
333
365
334
366
_tx_buffer[_tx_buffer_head] = c;
335
367
_tx_buffer_head = i;
0 commit comments