forked from SmingHub/Sming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
uart.cpp
1008 lines (841 loc) · 24.4 KB
/
uart.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
uart.cpp - esp8266 UART HAL
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@author 2018 mikee47 <mike@sillyhouse.net>
Additional features to support flexible transmit buffering and callbacks
*/
/**
* UART GPIOs
*
* UART0 TX: 1 or 2
* UART0 RX: 3
*
* UART0 SWAP TX: 15
* UART0 SWAP RX: 13
*
*
* UART1 TX: 7 (NC) or 2
* UART1 RX: 8 (NC)
*
* UART1 SWAP TX: 11 (NC)
* UART1 SWAP RX: 6 (NC)
*
* NC = Not Connected to Module Pads --> No Access
*
*/
#include <BitManipulations.h>
#include <driver/uart.h>
#include <espinc/uart_register.h>
#include <driver/SerialBuffer.h>
/*
* Parameters relating to RX FIFO and buffer thresholds
*
* 'headroom' is the number of characters which may be received before a receive overrun
* condition occurs and data is lost.
*
* For the hardware FIFO, data is processed via interrupt so the headroom can be fairly small.
* The greater the headroom, the more interrupts will be generated thus reducing efficiency.
*/
#define RX_FIFO_FULL_THRESHOLD 120 ///< UIFF interrupt when FIFO bytes > threshold
#define RX_FIFO_HEADROOM (UART_RX_FIFO_SIZE - RX_FIFO_FULL_THRESHOLD) ///< Chars between UIFF and UIOF
/*
* Using a buffer, data is typically processed via task callback so requires additional time.
* This figure is set to a nominal default which should provide robust operation for most situations.
* It can be adjusted if necessary via the rx_headroom parameter.
*/
#define DEFAULT_RX_HEADROOM (32 - RX_FIFO_HEADROOM)
namespace
{
int s_uart_debug_nr = UART_NO;
// Get number of characters in receive FIFO
__forceinline uint8_t uart_rxfifo_count(uint8_t nr)
{
return (READ_PERI_REG(UART_STATUS(nr)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT;
}
// Get number of characters in transmit FIFO
__forceinline uint8_t uart_txfifo_count(uint8_t nr)
{
return (READ_PERI_REG(UART_STATUS(nr)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT;
}
// Get available free characters in transmit FIFO
__forceinline uint8_t uart_txfifo_free(uint8_t nr)
{
return UART_TX_FIFO_SIZE - uart_txfifo_count(nr) - 1;
}
// Return true if transmit FIFO is full
__forceinline bool uart_txfifo_full(uint8_t nr)
{
return uart_txfifo_count(nr) >= (UART_TX_FIFO_SIZE - 1);
}
// Keep track of interrupt enable state for each UART
uint8_t isrMask;
// Keep a reference to all created UARTS - required because they share an ISR
smg_uart_t* uartInstances[UART_COUNT];
// Registered port callback functions
smg_uart_notify_callback_t notifyCallbacks[UART_COUNT];
/** @brief Invoke a port callback, if one has been registered
* @param uart
* @param code
*/
void notify(smg_uart_t* uart, smg_uart_notify_code_t code)
{
auto callback = notifyCallbacks[uart->uart_nr];
if(callback != nullptr) {
callback(uart, code);
}
}
__forceinline bool uart_isr_enabled(uint8_t nr)
{
return bitRead(isrMask, nr);
}
/** @brief Determine if the given uart is a real uart or a virtual one
*/
__forceinline bool is_physical(int uart_nr)
{
return (uart_nr >= 0) && (uart_nr < UART_PHYSICAL_COUNT);
}
__forceinline bool is_physical(smg_uart_t* uart)
{
return uart != nullptr && is_physical(uart->uart_nr);
}
/** @brief If given a virtual uart, obtain the related physical one
*/
smg_uart_t* get_physical(smg_uart_t* uart)
{
if(uart != nullptr && uart->uart_nr == UART2) {
uart = uartInstances[UART0];
}
return uart;
}
bool realloc_buffer(SerialBuffer*& buffer, size_t new_size)
{
if(buffer != nullptr) {
if(new_size == 0) {
smg_uart_disable_interrupts();
delete buffer;
buffer = nullptr;
smg_uart_restore_interrupts();
return true;
}
return buffer->resize(new_size) == new_size;
}
if(new_size == 0) {
return true;
}
auto new_buf = new SerialBuffer;
if(new_buf != nullptr && new_buf->resize(new_size) == new_size) {
buffer = new_buf;
return true;
}
delete new_buf;
return false;
}
/**
* @brief service interrupts for a UART
* @param uart_nr identifies which UART to check
* @param uart the allocated uart structure, which may be NULL if port hasn't been setup
*/
void IRAM_ATTR handle_uart_interrupt(uint8_t uart_nr, smg_uart_t* uart)
{
uint32_t usis = READ_PERI_REG(UART_INT_ST(uart_nr));
// If status is clear there's no interrupt to service on this UART
if(usis == 0) {
return;
}
/*
* If we haven't asked for interrupts on this UART, then disable all interrupt sources for it.
*
* This happens at startup where we've only initialised one of the UARTS. For example, we initialise
* UART1 for debug output but leave UART0 alone. However, the SDK has enabled some interrupt sources
* which we're not expecting.
*
* (Calling uart_detach_all() at startup pre-empts all this.)
*/
if(uart == nullptr || !uart_isr_enabled(uart_nr)) {
WRITE_PERI_REG(UART_INT_ENA(uart_nr), 0);
return;
}
// Value to be passed to callback
uint32_t status = usis;
// Deal with the event, unless we're in raw mode
if(!bitRead(uart->options, UART_OPT_CALLBACK_RAW)) {
// Rx FIFO full or timeout
if(usis & (UART_RXFIFO_FULL_INT_ST | UART_RXFIFO_TOUT_INT_ST | UART_RXFIFO_OVF_INT_ST)) {
size_t read = 0;
// Read as much data as possible from the RX FIFO into buffer
if(uart->rx_buffer != nullptr) {
size_t avail = uart_rxfifo_count(uart_nr);
size_t space = uart->rx_buffer->getFreeSpace();
read = (avail <= space) ? avail : space;
space -= read;
while(read-- != 0) {
uint8_t c = READ_PERI_REG(UART_FIFO(uart_nr));
uart->rx_buffer->writeChar(c);
}
// Don't call back until buffer is (almost) full
if(space > uart->rx_headroom) {
status &= ~UART_RXFIFO_FULL_INT_ST;
}
}
/*
* If the FIFO is full and we didn't read any of the data then need to mask the interrupt out or it'll recur.
* The interrupt gets re-enabled by a call to uart_read() or uart_flush()
*/
if(usis & UART_RXFIFO_OVF_INT_ST) {
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_nr), UART_RXFIFO_OVF_INT_ENA);
} else if(read == 0) {
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_nr), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
}
}
// Unless we replenish TX FIFO, disable after handling interrupt
if(usis & UART_TXFIFO_EMPTY_INT_ST) {
// Dump as much data as we can from buffer into the TX FIFO
if(uart->tx_buffer != nullptr) {
size_t space = uart_txfifo_free(uart_nr);
size_t avail = uart->tx_buffer->available();
size_t count = (avail <= space) ? avail : space;
while(count-- != 0) {
WRITE_PERI_REG(UART_FIFO(uart_nr), uart->tx_buffer->readChar());
}
}
// If TX FIFO remains empty then we must disable TX FIFO EMPTY interrupt to stop it recurring.
if(uart_txfifo_count(uart_nr) == 0) {
// The interrupt gets re-enabled by uart_write()
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_nr), UART_TXFIFO_EMPTY_INT_ENA);
} else {
// We've topped up TX FIFO so defer callback until next time
status &= ~UART_TXFIFO_EMPTY_INT_ST;
}
}
}
// Keep a note of persistent flags - cleared via uart_get_status()
uart->status |= status;
if(status != 0 && uart->callback != nullptr) {
uart->callback(uart, status);
}
// Final step is to clear status flags
WRITE_PERI_REG(UART_INT_CLR(uart_nr), usis);
}
/** @brief UART interrupt service routine
* @note both UARTS share the same ISR, although UART1 only supports transmit
*/
void IRAM_ATTR uart_isr(void* arg)
{
handle_uart_interrupt(UART0, uartInstances[UART0]);
handle_uart_interrupt(UART1, uartInstances[UART1]);
}
void uart0_pin_select(unsigned pin)
{
switch(pin) {
case 1:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_UART0_TXD);
break;
case 2:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_UART0_TXD_BK);
break;
case 3:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_UART0_RXD);
break;
case 13:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS);
break;
case 15:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_UART0_RTS);
break;
}
}
void uart0_pin_restore(unsigned pin)
{
switch(pin) {
case 1:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);
break;
case 2:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
break;
case 3:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3);
break;
case 13:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);
break;
case 15:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15);
break;
}
}
void uart1_pin_select(unsigned pin)
{
// GPIO7 as TX not possible! See GPIO pins used by UART
switch(pin) {
case 2:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_UART1_TXD_BK);
break;
}
}
void uart1_pin_restore(const unsigned pin)
{
switch(pin) {
case 2:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
break;
}
}
} // namespace
smg_uart_t* smg_uart_get_uart(uint8_t uart_nr)
{
return (uart_nr < UART_COUNT) ? uartInstances[uart_nr] : nullptr;
}
uint8_t smg_uart_disable_interrupts()
{
ETS_UART_INTR_DISABLE();
return isrMask;
}
void smg_uart_restore_interrupts()
{
if(isrMask != 0) {
ETS_UART_INTR_ENABLE();
}
}
bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback)
{
if(uart_nr >= UART_COUNT) {
return false;
}
notifyCallbacks[uart_nr] = callback;
return true;
}
void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* param)
{
if(uart != nullptr) {
uart->callback = nullptr; // In case interrupt fires between setting param and callback
uart->param = param;
uart->callback = callback;
}
}
size_t smg_uart_resize_rx_buffer(smg_uart_t* uart, size_t new_size)
{
if(smg_uart_rx_enabled(uart)) {
realloc_buffer(uart->rx_buffer, new_size);
}
return smg_uart_rx_buffer_size(uart);
}
size_t smg_uart_rx_buffer_size(smg_uart_t* uart)
{
return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->getSize() : 0;
}
size_t smg_uart_resize_tx_buffer(smg_uart_t* uart, size_t new_size)
{
if(smg_uart_tx_enabled(uart)) {
realloc_buffer(uart->tx_buffer, new_size);
}
return smg_uart_tx_buffer_size(uart);
}
size_t smg_uart_tx_buffer_size(smg_uart_t* uart)
{
return uart != nullptr && uart->tx_buffer != nullptr ? uart->tx_buffer->getSize() : 0;
}
int smg_uart_peek_char(smg_uart_t* uart)
{
return uart != nullptr && uart->rx_buffer ? uart->rx_buffer->peekChar() : -1;
}
int smg_uart_rx_find(smg_uart_t* uart, char c)
{
if(uart == nullptr || uart->rx_buffer == nullptr) {
return -1;
}
return uart->rx_buffer->find(c);
}
int smg_uart_peek_last_char(smg_uart_t* uart)
{
return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->peekLastChar() : -1;
}
size_t smg_uart_read(smg_uart_t* uart, void* buffer, size_t size)
{
if(!smg_uart_rx_enabled(uart) || buffer == nullptr || size == 0) {
return 0;
}
notify(uart, UART_NOTIFY_BEFORE_READ);
size_t read = 0;
auto buf = static_cast<uint8_t*>(buffer);
// First read data from RX buffer if in use
if(uart->rx_buffer != nullptr) {
while(read < size && !uart->rx_buffer->isEmpty())
buf[read++] = uart->rx_buffer->readChar();
}
// Top up from hardware FIFO
if(is_physical(uart)) {
while(read < size && uart_rxfifo_count(uart->uart_nr) != 0) {
buf[read++] = READ_PERI_REG(UART_FIFO(uart->uart_nr));
}
// FIFO full may have been disabled if buffer overflowed, re-enabled it now
WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr),
UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR | UART_RXFIFO_OVF_INT_CLR);
SET_PERI_REG_MASK(UART_INT_ENA(uart->uart_nr),
UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA | UART_RXFIFO_OVF_INT_ENA);
}
return read;
}
size_t smg_uart_rx_available(smg_uart_t* uart)
{
if(!smg_uart_rx_enabled(uart)) {
return 0;
}
smg_uart_disable_interrupts();
size_t avail = is_physical(uart) ? uart_rxfifo_count(uart->uart_nr) : 0;
if(uart->rx_buffer != nullptr) {
avail += uart->rx_buffer->available();
}
smg_uart_restore_interrupts();
return avail;
}
void smg_uart_start_isr(smg_uart_t* uart)
{
if(!is_physical(uart)) {
return;
}
uint32_t conf1 = 0;
uint32_t intena = 0;
if(smg_uart_rx_enabled(uart)) {
conf1 = (120 << UART_RXFIFO_FULL_THRHD_S) | (0x02 << UART_RX_TOUT_THRHD_S) | UART_RX_TOUT_EN;
/*
* There is little benefit in generating interrupts on errors, instead these
* should be cleared at the start of a transaction and checked at the end.
* See uart_get_status().
*/
intena = UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA | UART_BRK_DET_INT_ENA | UART_RXFIFO_OVF_INT_ENA;
}
if(smg_uart_tx_enabled(uart)) {
/*
* We can interrupt when TX FIFO is empty; at 1Mbit that gives us 800 CPU
* cycles before the last character has actually gone over the wire. Even if
* a gap occurs it is unlike to cause any problems. It also makes the callback
* more useful, for example if using it for RS485 we'd then want to reverse
* transfer direction and begin waiting for a response.
*/
// TX FIFO empty threshold
// conf1 |= (0 << UART_TXFIFO_EMPTY_THRHD_S);
// TX FIFO empty interrupt only gets enabled via uart_write function()
}
WRITE_PERI_REG(UART_CONF1(uart->uart_nr), conf1);
WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr), 0xffff);
WRITE_PERI_REG(UART_INT_ENA(uart->uart_nr), intena);
uint8_t oldmask = isrMask;
bitSet(isrMask, uart->uart_nr);
if(oldmask == 0) {
ETS_UART_INTR_DISABLE();
ETS_UART_INTR_ATTACH(uart_isr, nullptr);
ETS_UART_INTR_ENABLE();
}
}
size_t smg_uart_write(smg_uart_t* uart, const void* buffer, size_t size)
{
if(!smg_uart_tx_enabled(uart) || buffer == nullptr || size == 0) {
return 0;
}
size_t written = 0;
auto buf = static_cast<const uint8_t*>(buffer);
bool isPhysical = is_physical(uart);
while(written < size) {
if(isPhysical) {
// If TX buffer not in use or it's empty then write directly to hardware FIFO
if(uart->tx_buffer == nullptr || uart->tx_buffer->isEmpty()) {
while(written < size && !uart_txfifo_full(uart->uart_nr)) {
WRITE_PERI_REG(UART_FIFO(uart->uart_nr), buf[written++]);
}
// Enable TX FIFO EMPTY interrupt
WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr), UART_TXFIFO_EMPTY_INT_CLR);
SET_PERI_REG_MASK(UART_INT_ENA(uart->uart_nr), UART_TXFIFO_EMPTY_INT_ENA);
}
}
// Write any remaining data into transmit buffer
if(uart->tx_buffer != nullptr) {
while(written < size && uart->tx_buffer->writeChar(buf[written])) {
++written;
}
}
notify(uart, UART_NOTIFY_AFTER_WRITE);
if(!bitRead(uart->options, UART_OPT_TXWAIT)) {
break;
}
}
return written;
}
size_t smg_uart_tx_free(smg_uart_t* uart)
{
if(!smg_uart_tx_enabled(uart)) {
return 0;
}
smg_uart_disable_interrupts();
size_t space = is_physical(uart) ? uart_txfifo_free(uart->uart_nr) : 0;
if(uart->tx_buffer != nullptr) {
space += uart->tx_buffer->getFreeSpace();
}
smg_uart_restore_interrupts();
return space;
}
void smg_uart_wait_tx_empty(smg_uart_t* uart)
{
if(!smg_uart_tx_enabled(uart)) {
return;
}
notify(uart, UART_NOTIFY_WAIT_TX);
if(uart->tx_buffer != nullptr) {
while(!uart->tx_buffer->isEmpty()) {
system_soft_wdt_feed();
}
}
if(is_physical(uart)) {
while(uart_txfifo_count(uart->uart_nr) != 0)
system_soft_wdt_feed();
}
}
void smg_uart_set_break(smg_uart_t* uart, bool state)
{
uart = get_physical(uart);
if(uart != nullptr) {
if(state) {
SET_PERI_REG_MASK(UART_CONF0(uart->uart_nr), UART_TXD_BRK);
} else {
CLEAR_PERI_REG_MASK(UART_CONF0(uart->uart_nr), UART_TXD_BRK);
}
}
}
uint8_t smg_uart_get_status(smg_uart_t* uart)
{
uint8_t status = 0;
if(uart != nullptr) {
smg_uart_disable_interrupts();
// Get break/overflow flags from actual uart (physical or otherwise)
status = uart->status & (UART_BRK_DET_INT_ST | UART_RXFIFO_OVF_INT_ST);
uart->status = 0;
// Read raw status register directly from real uart, masking out non-error bits
uart = get_physical(uart);
if(uart != nullptr) {
uint32_t intraw = READ_PERI_REG(UART_INT_RAW(uart->uart_nr));
intraw &= UART_BRK_DET_INT_ST | UART_RXFIFO_OVF_INT_ST | UART_FRM_ERR_INT_ST | UART_PARITY_ERR_INT_ST;
status |= intraw;
// Clear errors
WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr), status);
}
smg_uart_restore_interrupts();
}
return status;
}
void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode)
{
if(uart == nullptr) {
return;
}
bool flushRx = mode != UART_TX_ONLY && uart->mode != UART_TX_ONLY;
bool flushTx = mode != UART_RX_ONLY && uart->mode != UART_RX_ONLY;
smg_uart_disable_interrupts();
if(flushRx && uart->rx_buffer != nullptr) {
uart->rx_buffer->clear();
}
if(flushTx && uart->tx_buffer != nullptr) {
uart->tx_buffer->clear();
}
if(is_physical(uart)) {
// Clear the hardware FIFOs
uint32_t flushBits = 0;
if(flushTx) {
flushBits |= UART_TXFIFO_RST;
}
if(flushRx) {
flushBits |= UART_RXFIFO_RST;
}
SET_PERI_REG_MASK(UART_CONF0(uart->uart_nr), flushBits);
CLEAR_PERI_REG_MASK(UART_CONF0(uart->uart_nr), flushBits);
if(flushTx) {
// Prevent TX FIFO EMPTY interrupts - don't need them until uart_write is called again
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart->uart_nr), UART_TXFIFO_EMPTY_INT_ENA);
}
// If receive overflow occurred then these interrupts will be masked
if(flushRx) {
WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr), ~UART_TXFIFO_EMPTY_INT_CLR);
SET_PERI_REG_MASK(UART_INT_ENA(uart->uart_nr),
UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA | UART_RXFIFO_OVF_INT_ENA);
}
}
smg_uart_restore_interrupts();
}
uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate)
{
if(!is_physical(uart_nr) || baud_rate == 0) {
return 0;
}
uint32_t clkdiv = UART_CLK_FREQ / baud_rate;
WRITE_PERI_REG(UART_CLKDIV(uart_nr), clkdiv);
// Return the actual baud rate in use
baud_rate = clkdiv ? UART_CLK_FREQ / clkdiv : 0;
return baud_rate;
}
uint32_t smg_uart_set_baudrate(smg_uart_t* uart, uint32_t baud_rate)
{
uart = get_physical(uart);
if(uart == nullptr) {
return 0;
}
baud_rate = smg_uart_set_baudrate_reg(uart->uart_nr, baud_rate);
// Store the actual baud rate in use
uart->baud_rate = baud_rate;
return baud_rate;
}
uint32_t smg_uart_get_baudrate(smg_uart_t* uart)
{
uart = get_physical(uart);
return (uart == nullptr) ? 0 : uart->baud_rate;
}
smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg)
{
// Already initialised?
if(smg_uart_get_uart(cfg.uart_nr) != nullptr) {
return nullptr;
}
auto uart = new smg_uart_t;
if(uart == nullptr) {
return nullptr;
}
memset(uart, 0, sizeof(smg_uart_t));
uart->uart_nr = cfg.uart_nr;
uart->mode = cfg.mode;
uart->options = cfg.options;
uart->tx_pin = UART_PIN_DEFAULT;
uart->rx_pin = UART_PIN_DEFAULT;
uart->rx_headroom = DEFAULT_RX_HEADROOM;
auto rxBufferSize = cfg.rx_size;
auto txBufferSize = cfg.tx_size;
switch(cfg.uart_nr) {
case UART0:
case UART2:
// Virtual uart requires a minimum RAM buffer
if(cfg.uart_nr == UART2) {
rxBufferSize += UART_RX_FIFO_SIZE;
txBufferSize += UART_TX_FIFO_SIZE;
}
if(smg_uart_rx_enabled(uart) && !realloc_buffer(uart->rx_buffer, rxBufferSize)) {
delete uart;
return nullptr;
}
if(smg_uart_tx_enabled(uart) && !realloc_buffer(uart->tx_buffer, txBufferSize)) {
delete uart->rx_buffer;
delete uart;
return nullptr;
}
if(cfg.uart_nr == UART2) {
break;
}
// OK, buffers allocated so setup hardware
smg_uart_detach(cfg.uart_nr);
if(smg_uart_rx_enabled(uart)) {
uart->rx_pin = 3;
uart0_pin_select(uart->rx_pin);
}
if(smg_uart_tx_enabled(uart)) {
uart->tx_pin = (cfg.tx_pin == 2) ? 2 : 1;
uart0_pin_select(uart->tx_pin);
}
CLEAR_PERI_REG_MASK(UART_SWAP_REG, UART_SWAP0);
WRITE_PERI_REG(UART_CONF0(UART0), cfg.config);
break;
case UART1:
// Note: uart_interrupt_handler does not support RX on UART 1
if(uart->mode == UART_RX_ONLY) {
delete uart;
return nullptr;
}
uart->mode = UART_TX_ONLY;
// Transmit buffer optional
if(!realloc_buffer(uart->tx_buffer, txBufferSize)) {
delete uart;
return nullptr;
}
// Setup hardware
smg_uart_detach(cfg.uart_nr);
uart->tx_pin = 2;
uart1_pin_select(uart->tx_pin);
WRITE_PERI_REG(UART_CONF0(UART1), cfg.config);
break;
default:
// big fail!
delete uart;
return nullptr;
}
smg_uart_set_baudrate(uart, cfg.baudrate);
smg_uart_flush(uart);
uartInstances[cfg.uart_nr] = uart;
smg_uart_start_isr(uart);
notify(uart, UART_NOTIFY_AFTER_OPEN);
return uart;
}
void smg_uart_uninit(smg_uart_t* uart)
{
if(uart == nullptr) {
return;
}
notify(uart, UART_NOTIFY_BEFORE_CLOSE);
smg_uart_stop_isr(uart);
// If debug output being sent to this UART, disable it
if(uart->uart_nr == s_uart_debug_nr) {
smg_uart_set_debug(UART_NO);
}
switch(uart->uart_nr) {
case UART0:
uart0_pin_restore(uart->rx_pin);
uart0_pin_restore(uart->tx_pin);
break;
case UART1:
uart1_pin_restore(uart->tx_pin);
break;
}
delete uart->rx_buffer;
delete uart->tx_buffer;
delete uart;
}
smg_uart_t* smg_uart_init(uint8_t uart_nr, uint32_t baudrate, uint32_t config, smg_uart_mode_t mode, uint8_t tx_pin,
size_t rx_size, size_t tx_size)
{
smg_uart_config_t cfg = {
.uart_nr = uart_nr,
.tx_pin = tx_pin,
.rx_pin = UART_PIN_DEFAULT,
.mode = mode,
.options = _BV(UART_OPT_TXWAIT),
.baudrate = baudrate,
.config = config,
.rx_size = rx_size,
.tx_size = tx_size,
};
return smg_uart_init_ex(cfg);
}
void smg_uart_swap(smg_uart_t* uart, int tx_pin)
{
if(uart == nullptr) {
return;
}
switch(uart->uart_nr) {
case UART0:
uart0_pin_restore(uart->tx_pin);
uart0_pin_restore(uart->rx_pin);
if(uart->tx_pin == 1 || uart->tx_pin == 2 || uart->rx_pin == 3) {
if(smg_uart_tx_enabled(uart)) {
uart->tx_pin = 15;
}
if(smg_uart_rx_enabled(uart)) {
uart->rx_pin = 13;
}
SET_PERI_REG_MASK(UART_SWAP_REG, UART_SWAP0);
} else {
if(smg_uart_tx_enabled(uart)) {
uart->tx_pin = (tx_pin == 2) ? 2 : 1;
}
if(smg_uart_rx_enabled(uart)) {
uart->rx_pin = 3;
}
CLEAR_PERI_REG_MASK(UART_SWAP_REG, UART_SWAP0);
}
uart0_pin_select(uart->tx_pin);
uart0_pin_select(uart->rx_pin);
break;
case UART1:
// Currently no swap possible! See GPIO pins used by UART
break;
default:
break;
}
}
bool smg_uart_set_tx(smg_uart_t* uart, int tx_pin)
{
if(uart != nullptr && uart->uart_nr == UART0 && smg_uart_tx_enabled(uart)) {
uart0_pin_restore(uart->tx_pin);
uart->tx_pin = (tx_pin == 2) ? 2 : 1;
uart0_pin_select(uart->tx_pin);
return true;
}
// All other combinations, e.g. GPIO7 as TX not possible! See GPIO pins used by UART
return false;
}
bool smg_uart_set_pins(smg_uart_t* uart, int tx_pin, int rx_pin)
{
if(uart == nullptr) {
return false;
}
// Only UART0 allows pin changes
if(uart->uart_nr != UART0) {
return false;
}
bool res{true};
if(smg_uart_tx_enabled(uart) && uart->tx_pin != tx_pin) {
if(rx_pin == 13 && tx_pin == 15) {
smg_uart_swap(uart, 15);
} else if(rx_pin == 3 && (tx_pin == 1 || tx_pin == 2)) {
if(uart->rx_pin != rx_pin) {
smg_uart_swap(uart, tx_pin);
} else {
smg_uart_set_tx(uart, tx_pin);
}
} else {
res = false;
}
}
if(smg_uart_rx_enabled(uart) && uart->rx_pin != rx_pin) {
if(rx_pin == 13 && tx_pin == 15) {
smg_uart_swap(uart, 15);
} else {
res = false;
}
}
return res;
}
void smg_uart_debug_putc(char c)
{
smg_uart_t* uart = smg_uart_get_uart(s_uart_debug_nr);
if(uart != nullptr) {
smg_uart_write_char(uart, c);
}
}
void smg_uart_set_debug(int uart_nr)
{
s_uart_debug_nr = uart_nr;
system_set_os_print(uart_nr >= 0);
ets_install_putc1(smg_uart_debug_putc);
}
int smg_uart_get_debug()
{
return s_uart_debug_nr;
}
void smg_uart_detach(int uart_nr)
{
if(!is_physical(uart_nr)) {
return;
}
smg_uart_disable_interrupts();
bitClear(isrMask, uart_nr);
WRITE_PERI_REG(UART_CONF1(uart_nr), 0);
WRITE_PERI_REG(UART_INT_CLR(uart_nr), 0xffff);
WRITE_PERI_REG(UART_INT_ENA(uart_nr), 0);
smg_uart_restore_interrupts();
}
void smg_uart_detach_all()
{