@@ -101,24 +101,38 @@ void UartClass::_tx_data_empty_irq(void)
101101 // buffer. Send the next byte
102102 unsigned char c = _tx_buffer[_tx_buffer_tail];
103103 _tx_buffer_tail = (_tx_buffer_tail + 1 ) % SERIAL_TX_BUFFER_SIZE;
104-
105- (*_hwserial_module).TXDATAL = c;
106-
104+
107105 // clear the TXCIF flag -- "can be cleared by writing a one to its bit
108106 // location". This makes sure flush() won't return until the bytes
109107 // 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+ }
116124}
117125
118126// Public Methods //////////////////////////////////////////////////////////////
119127
120128void UartClass::begin (unsigned long baud, uint16_t config)
121129{
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+
122136 // uint16_t baud_setting = 0;
123137 int32_t baud_setting = 0 ;
124138 uint8_t error = 0 ;
@@ -127,16 +141,6 @@ void UartClass::begin(unsigned long baud, uint16_t config)
127141 uint8_t oldSREG = SREG;
128142 cli ();
129143
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-
140144 // ********Check if desired baud rate is within the acceptable range for using CLK2X RX-mode********
141145 // Condition from datasheet
142146 // This limits the minimum baud_setting value to 64 (0x0040)
@@ -178,7 +182,7 @@ void UartClass::begin(unsigned long baud, uint16_t config)
178182 error = 1 ;
179183 }
180184
181- // Do nothing if an invalid baud rate is requested
185+ // Do nothing if an invalid baud rate is requested
182186 if (!error) {
183187
184188#ifdef PERFORM_BAUD_CORRECTION
@@ -199,15 +203,25 @@ void UartClass::begin(unsigned long baud, uint16_t config)
199203 }
200204#endif
201205
202- // assign the baud_setting, a.k.a. BAUD (USART Baud Rate Register)
203- (*_hwserial_module).BAUD = (int16_t ) baud_setting;
204-
205206 _written = false ;
206207
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);
208214
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+
209224 (*_hwserial_module).CTRLA |= USART_RXCIE_bm;
210- (*_hwserial_module).CTRLB |= (USART_RXEN_bm | USART_TXEN_bm);
211225 }
212226
213227 // Restore SREG content
@@ -277,16 +291,25 @@ void UartClass::flush()
277291 if (!_written) {
278292 return ;
279293 }
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+ }
280305
281306 // Spin until the data-register-empty-interrupt is disabled and TX complete interrupt flag is raised
282307 while ( ((*_hwserial_module).CTRLA & USART_DREIE_bm) || (!((*_hwserial_module).STATUS & USART_TXCIF_bm)) ) {
283308
284309 // If interrupts are globally disabled or the and DR empty interrupt is disabled,
285310 // 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 ();
290313 }
291314 }
292315 // If we get here, nothing is queued anymore (DREIE is disabled) and
@@ -303,7 +326,7 @@ size_t UartClass::write(uint8_t c)
303326 // 500kbit/s) bit rates, where interrupt overhead becomes a slowdown.
304327 if ( (_tx_buffer_head == _tx_buffer_tail) && ((*_hwserial_module).STATUS & USART_DREIF_bm) ) {
305328 (*_hwserial_module).TXDATAL = c;
306- (*_hwserial_module).STATUS | = USART_TXCIF_bm;
329+ (*_hwserial_module).STATUS = USART_TXCIF_bm;
307330
308331 // Make sure data register empty interrupt is disabled to avoid
309332 // that the interrupt handler is called in this situation
@@ -312,24 +335,33 @@ size_t UartClass::write(uint8_t c)
312335 return 1 ;
313336 }
314337
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+
315349 tx_buffer_index_t i = (_tx_buffer_head + 1 ) % SERIAL_TX_BUFFER_SIZE;
316350
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+ }
333365
334366 _tx_buffer[_tx_buffer_head] = c;
335367 _tx_buffer_head = i;
0 commit comments