aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/uarte.rs
diff options
context:
space:
mode:
authorhuntc <[email protected]>2022-10-09 13:07:25 +1100
committerhuntc <[email protected]>2022-10-09 13:07:25 +1100
commite1faf8860776f6ad2bac2f3b06e7160fe00da7df (patch)
tree72ae88efc2d86e7dbdccee5f5dd7c8d6676a25cf /embassy-nrf/src/uarte.rs
parentf8fd6ab208fd09142ab9789078e9b23ba8c3e6a9 (diff)
Removes some of the code duplication for UarteWithIdle
This commit removes some of the code duplication for UarteWithIdle at the expense of requiring a split. As the example illustrates though, this expense seems worth the benefit in terms of maintenance, and the avoidance of copying over methods. My main motivation for this commit was actually due to the `event_endtx` method not having been copied across.
Diffstat (limited to 'embassy-nrf/src/uarte.rs')
-rw-r--r--embassy-nrf/src/uarte.rs398
1 files changed, 131 insertions, 267 deletions
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
index d99599112..636d6c7a3 100644
--- a/embassy-nrf/src/uarte.rs
+++ b/embassy-nrf/src/uarte.rs
@@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> {
173 (self.tx, self.rx) 173 (self.tx, self.rx)
174 } 174 }
175 175
176 /// Split the Uarte into a transmitter and receiver that will
177 /// return on idle, which is determined as the time it takes
178 /// for two bytes to be received.
179 pub fn split_with_idle<U: TimerInstance>(
180 self,
181 timer: impl Peripheral<P = U> + 'd,
182 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
183 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
184 ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) {
185 let mut timer = Timer::new(timer);
186
187 into_ref!(ppi_ch1, ppi_ch2);
188
189 let r = T::regs();
190
191 // BAUDRATE register values are `baudrate * 2^32 / 16000000`
192 // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
193 //
194 // We want to stop RX if line is idle for 2 bytes worth of time
195 // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
196 // This gives us the amount of 16M ticks for 20 bits.
197 let baudrate = r.baudrate.read().baudrate().variant().unwrap();
198 let timeout = 0x8000_0000 / (baudrate as u32 / 40);
199
200 timer.set_frequency(Frequency::F16MHz);
201 timer.cc(0).write(timeout);
202 timer.cc(0).short_compare_clear();
203 timer.cc(0).short_compare_stop();
204
205 let mut ppi_ch1 = Ppi::new_one_to_two(
206 ppi_ch1.map_into(),
207 Event::from_reg(&r.events_rxdrdy),
208 timer.task_clear(),
209 timer.task_start(),
210 );
211 ppi_ch1.enable();
212
213 let mut ppi_ch2 = Ppi::new_one_to_one(
214 ppi_ch2.map_into(),
215 timer.cc(0).event_compare(),
216 Task::from_reg(&r.tasks_stoprx),
217 );
218 ppi_ch2.enable();
219
220 (
221 self.tx,
222 UarteRxWithIdle {
223 rx: self.rx,
224 timer,
225 ppi_ch1: ppi_ch1,
226 _ppi_ch2: ppi_ch2,
227 },
228 )
229 }
230
176 /// Return the endtx event for use with PPI 231 /// Return the endtx event for use with PPI
177 pub fn event_endtx(&self) -> Event { 232 pub fn event_endtx(&self) -> Event {
178 let r = T::regs(); 233 let r = T::regs();
@@ -597,236 +652,14 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
597 } 652 }
598} 653}
599 654
600#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] 655pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
601pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { 656 rx: UarteRx<'d, T>,
602 // Do nothing 657 timer: Timer<'d, U>,
603}
604
605#[cfg(any(feature = "_nrf9160", feature = "nrf5340"))]
606pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::RegisterBlock) {
607 use core::ops::Deref;
608
609 // Apply workaround for anomalies:
610 // - nRF9160 - anomaly 23
611 // - nRF5340 - anomaly 44
612 let rxenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x564) as *const u32;
613 let txenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x568) as *const u32;
614
615 // NB Safety: This is taken from Nordic's driver -
616 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
617 if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 {
618 r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
619 }
620
621 // NB Safety: This is taken from Nordic's driver -
622 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
623 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 {
624 r.enable.write(|w| w.enable().enabled());
625 r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
626
627 let mut workaround_succeded = false;
628 // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered.
629 // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured
630 // (resulting in 12 bits per data byte sent), this may take up to 40 ms.
631 for _ in 0..40000 {
632 // NB Safety: This is taken from Nordic's driver -
633 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
634 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 {
635 workaround_succeded = true;
636 break;
637 } else {
638 // Need to sleep for 1us here
639 }
640 }
641
642 if !workaround_succeded {
643 panic!("Failed to apply workaround for UART");
644 }
645
646 let errors = r.errorsrc.read().bits();
647 // NB Safety: safe to write back the bits we just read to clear them
648 r.errorsrc.write(|w| unsafe { w.bits(errors) });
649 r.enable.write(|w| w.enable().disabled());
650 }
651}
652
653pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
654 if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 {
655 // Finally we can disable, and we do so for the peripheral
656 // i.e. not just rx concerns.
657 r.enable.write(|w| w.enable().disabled());
658
659 gpio::deconfigure_pin(r.psel.rxd.read().bits());
660 gpio::deconfigure_pin(r.psel.txd.read().bits());
661 gpio::deconfigure_pin(r.psel.rts.read().bits());
662 gpio::deconfigure_pin(r.psel.cts.read().bits());
663
664 trace!("uarte tx and rx drop: done");
665 }
666}
667
668/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels,
669/// allowing it to implement the ReadUntilIdle trait.
670pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
671 tx: UarteTx<'d, U>,
672 rx: UarteRxWithIdle<'d, U, T>,
673}
674
675impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
676 /// Create a new UARTE without hardware flow control
677 pub fn new(
678 uarte: impl Peripheral<P = U> + 'd,
679 timer: impl Peripheral<P = T> + 'd,
680 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
681 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
682 irq: impl Peripheral<P = U::Interrupt> + 'd,
683 rxd: impl Peripheral<P = impl GpioPin> + 'd,
684 txd: impl Peripheral<P = impl GpioPin> + 'd,
685 config: Config,
686 ) -> Self {
687 into_ref!(rxd, txd);
688 Self::new_inner(
689 uarte,
690 timer,
691 ppi_ch1,
692 ppi_ch2,
693 irq,
694 rxd.map_into(),
695 txd.map_into(),
696 None,
697 None,
698 config,
699 )
700 }
701
702 /// Create a new UARTE with hardware flow control (RTS/CTS)
703 pub fn new_with_rtscts(
704 uarte: impl Peripheral<P = U> + 'd,
705 timer: impl Peripheral<P = T> + 'd,
706 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
707 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
708 irq: impl Peripheral<P = U::Interrupt> + 'd,
709 rxd: impl Peripheral<P = impl GpioPin> + 'd,
710 txd: impl Peripheral<P = impl GpioPin> + 'd,
711 cts: impl Peripheral<P = impl GpioPin> + 'd,
712 rts: impl Peripheral<P = impl GpioPin> + 'd,
713 config: Config,
714 ) -> Self {
715 into_ref!(rxd, txd, cts, rts);
716 Self::new_inner(
717 uarte,
718 timer,
719 ppi_ch1,
720 ppi_ch2,
721 irq,
722 rxd.map_into(),
723 txd.map_into(),
724 Some(cts.map_into()),
725 Some(rts.map_into()),
726 config,
727 )
728 }
729
730 fn new_inner(
731 uarte: impl Peripheral<P = U> + 'd,
732 timer: impl Peripheral<P = T> + 'd,
733 ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
734 ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
735 irq: impl Peripheral<P = U::Interrupt> + 'd,
736 rxd: PeripheralRef<'d, AnyPin>,
737 txd: PeripheralRef<'d, AnyPin>,
738 cts: Option<PeripheralRef<'d, AnyPin>>,
739 rts: Option<PeripheralRef<'d, AnyPin>>,
740 config: Config,
741 ) -> Self {
742 let baudrate = config.baudrate;
743 let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split();
744
745 let mut timer = Timer::new(timer);
746
747 into_ref!(ppi_ch1, ppi_ch2);
748
749 let r = U::regs();
750
751 // BAUDRATE register values are `baudrate * 2^32 / 16000000`
752 // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
753 //
754 // We want to stop RX if line is idle for 2 bytes worth of time
755 // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
756 // This gives us the amount of 16M ticks for 20 bits.
757 let timeout = 0x8000_0000 / (baudrate as u32 / 40);
758
759 timer.set_frequency(Frequency::F16MHz);
760 timer.cc(0).write(timeout);
761 timer.cc(0).short_compare_clear();
762 timer.cc(0).short_compare_stop();
763
764 let mut ppi_ch1 = Ppi::new_one_to_two(
765 ppi_ch1.map_into(),
766 Event::from_reg(&r.events_rxdrdy),
767 timer.task_clear(),
768 timer.task_start(),
769 );
770 ppi_ch1.enable();
771
772 let mut ppi_ch2 = Ppi::new_one_to_one(
773 ppi_ch2.map_into(),
774 timer.cc(0).event_compare(),
775 Task::from_reg(&r.tasks_stoprx),
776 );
777 ppi_ch2.enable();
778
779 Self {
780 tx,
781 rx: UarteRxWithIdle {
782 rx,
783 timer,
784 ppi_ch1: ppi_ch1,
785 _ppi_ch2: ppi_ch2,
786 },
787 }
788 }
789
790 /// Split the Uarte into a transmitter and receiver, which is
791 /// particuarly useful when having two tasks correlating to
792 /// transmitting and receiving.
793 pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) {
794 (self.tx, self.rx)
795 }
796
797 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
798 self.rx.read(buffer).await
799 }
800
801 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
802 self.tx.write(buffer).await
803 }
804
805 pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
806 self.rx.blocking_read(buffer)
807 }
808
809 pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
810 self.tx.blocking_write(buffer)
811 }
812
813 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
814 self.rx.read_until_idle(buffer).await
815 }
816
817 pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
818 self.rx.blocking_read_until_idle(buffer)
819 }
820}
821
822pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> {
823 rx: UarteRx<'d, U>,
824 timer: Timer<'d, T>,
825 ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, 658 ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
826 _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, 659 _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
827} 660}
828 661
829impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> { 662impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
830 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 663 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
831 self.ppi_ch1.disable(); 664 self.ppi_ch1.disable();
832 self.rx.read(buffer).await 665 self.rx.read(buffer).await
@@ -848,8 +681,8 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
848 let ptr = buffer.as_ptr(); 681 let ptr = buffer.as_ptr();
849 let len = buffer.len(); 682 let len = buffer.len();
850 683
851 let r = U::regs(); 684 let r = T::regs();
852 let s = U::state(); 685 let s = T::state();
853 686
854 self.ppi_ch1.enable(); 687 self.ppi_ch1.enable();
855 688
@@ -904,7 +737,7 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
904 let ptr = buffer.as_ptr(); 737 let ptr = buffer.as_ptr();
905 let len = buffer.len(); 738 let len = buffer.len();
906 739
907 let r = U::regs(); 740 let r = T::regs();
908 741
909 self.ppi_ch1.enable(); 742 self.ppi_ch1.enable();
910 743
@@ -929,6 +762,75 @@ impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
929 Ok(n) 762 Ok(n)
930 } 763 }
931} 764}
765
766#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))]
767pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) {
768 // Do nothing
769}
770
771#[cfg(any(feature = "_nrf9160", feature = "nrf5340"))]
772pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::RegisterBlock) {
773 use core::ops::Deref;
774
775 // Apply workaround for anomalies:
776 // - nRF9160 - anomaly 23
777 // - nRF5340 - anomaly 44
778 let rxenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x564) as *const u32;
779 let txenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x568) as *const u32;
780
781 // NB Safety: This is taken from Nordic's driver -
782 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
783 if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 {
784 r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
785 }
786
787 // NB Safety: This is taken from Nordic's driver -
788 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
789 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 {
790 r.enable.write(|w| w.enable().enabled());
791 r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
792
793 let mut workaround_succeded = false;
794 // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered.
795 // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured
796 // (resulting in 12 bits per data byte sent), this may take up to 40 ms.
797 for _ in 0..40000 {
798 // NB Safety: This is taken from Nordic's driver -
799 // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
800 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 {
801 workaround_succeded = true;
802 break;
803 } else {
804 // Need to sleep for 1us here
805 }
806 }
807
808 if !workaround_succeded {
809 panic!("Failed to apply workaround for UART");
810 }
811
812 let errors = r.errorsrc.read().bits();
813 // NB Safety: safe to write back the bits we just read to clear them
814 r.errorsrc.write(|w| unsafe { w.bits(errors) });
815 r.enable.write(|w| w.enable().disabled());
816 }
817}
818
819pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
820 if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 {
821 // Finally we can disable, and we do so for the peripheral
822 // i.e. not just rx concerns.
823 r.enable.write(|w| w.enable().disabled());
824
825 gpio::deconfigure_pin(r.psel.rxd.read().bits());
826 gpio::deconfigure_pin(r.psel.txd.read().bits());
827 gpio::deconfigure_pin(r.psel.rts.read().bits());
828 gpio::deconfigure_pin(r.psel.cts.read().bits());
829
830 trace!("uarte tx and rx drop: done");
831 }
832}
833
932pub(crate) mod sealed { 834pub(crate) mod sealed {
933 use core::sync::atomic::AtomicU8; 835 use core::sync::atomic::AtomicU8;
934 836
@@ -1006,18 +908,6 @@ mod eh02 {
1006 Ok(()) 908 Ok(())
1007 } 909 }
1008 } 910 }
1009
1010 impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write<u8> for UarteWithIdle<'d, U, T> {
1011 type Error = Error;
1012
1013 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
1014 self.blocking_write(buffer)
1015 }
1016
1017 fn bflush(&mut self) -> Result<(), Self::Error> {
1018 Ok(())
1019 }
1020 }
1021} 911}
1022 912
1023#[cfg(feature = "unstable-traits")] 913#[cfg(feature = "unstable-traits")]
@@ -1067,10 +957,6 @@ mod eh1 {
1067 impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { 957 impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> {
1068 type Error = Error; 958 type Error = Error;
1069 } 959 }
1070
1071 impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> {
1072 type Error = Error;
1073 }
1074} 960}
1075 961
1076#[cfg(all( 962#[cfg(all(
@@ -1126,26 +1012,4 @@ mod eha {
1126 self.read(buffer) 1012 self.read(buffer)
1127 } 1013 }
1128 } 1014 }
1129
1130 impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> {
1131 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1132
1133 fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
1134 self.read(buffer)
1135 }
1136 }
1137
1138 impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> {
1139 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1140
1141 fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
1142 self.write(buffer)
1143 }
1144
1145 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
1146
1147 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
1148 async move { Ok(()) }
1149 }
1150 }
1151} 1015}