2323#include "./uart_ev3.h"
2424#include "./uart_ev3_pru.h"
2525
26- #include <tiam1808/uart.h>
27- #include <tiam1808/psc.h>
28- #include <tiam1808/hw/soc_AM1808.h>
29- #include <tiam1808/hw/hw_types.h>
30- #include <tiam1808/hw/hw_syscfg0_AM1808.h>
26+ #include <tiam1808/armv5/am1808/edma_event.h>
3127#include <tiam1808/armv5/am1808/interrupt.h>
28+ #include <tiam1808/edma.h>
29+ #include <tiam1808/hw/hw_edma3cc.h>
30+ #include <tiam1808/hw/hw_syscfg0_AM1808.h>
31+ #include <tiam1808/hw/hw_types.h>
32+ #include <tiam1808/hw/soc_AM1808.h>
33+ #include <tiam1808/psc.h>
34+ #include <tiam1808/uart.h>
3235
3336/**
3437 * This file contains two almost entirely separate implementations of UART
@@ -126,56 +129,10 @@ pbio_error_t pbdrv_uart_read(pbio_os_state_t *state, pbdrv_uart_dev_t *uart, uin
126129 PBIO_OS_ASYNC_END (PBIO_SUCCESS );
127130}
128131
129- /**
130- * Helper function to write one byte in the write protothread.
131- *
132- * @param [in] uart The UART device.
133- * @param [in] byte The byte to write.
134- * @return True if the byte was written, false otherwise.
135- */
136- static bool pbdrv_uart_try_to_write_byte (pbdrv_uart_dev_t * uart , uint8_t byte ) {
137- const pbdrv_uart_ev3_platform_data_t * pdata = uart -> pdata ;
138-
139- // Attempt to write one byte with the hardware UART.
140- if (pdata -> uart_kind == EV3_UART_HW ) {
141- return UARTCharPutNonBlocking (pdata -> base_address , byte );
142- }
143-
144- // Otherwise, attempt to write one byte with the PRU UART.
145- // We could be sending more than one byte at a time, but we keep it
146- // consistent with the hardware UART limitations for simplicity, since
147- // EV3 sensors typically don't send much data anyway.
148- return pbdrv_uart_ev3_pru_write_bytes (pdata -> peripheral_id , & byte , 1 );
149- }
132+ static pbio_error_t pbdrv_uart_write_pru (pbio_os_state_t * state , pbdrv_uart_dev_t * uart , uint8_t * msg , uint8_t length , uint32_t timeout ) {
150133
151- /**
152- * Helper function to check if the UART is ready to write.
153- *
154- * @param [in] uart The UART device.
155- * @return True if the UART is ready to write, false otherwise.
156- */
157- static bool pbdrv_uart_can_write (pbdrv_uart_dev_t * uart ) {
158134 const pbdrv_uart_ev3_platform_data_t * pdata = uart -> pdata ;
159135
160- // Check UART_LSR for THR_EMPTY
161- if (pdata -> uart_kind == EV3_UART_HW ) {
162- // Always call back after this. This creates a (yielding) poll loop for
163- // writing. This works around the unreliable TX_EMPTY interrupt when
164- // reading and writing simultaneously: Reading the status during an RX
165- // interrupt clears the TX_EMPTY interrupt, which is not re-triggered.
166- // Since UART writes on EV3 are limited to small messages like sensor
167- // mode changes, this is acceptable.
168- pbio_os_request_poll ();
169- return UARTSpaceAvail (pdata -> base_address );
170- }
171-
172- // For PRU UART, we rely on the flag set by its IRQ handler.
173- // So no need to poll here.
174- return pbdrv_uart_ev3_pru_can_write (pdata -> peripheral_id );
175- }
176-
177- pbio_error_t pbdrv_uart_write (pbio_os_state_t * state , pbdrv_uart_dev_t * uart , uint8_t * msg , uint8_t length , uint32_t timeout ) {
178-
179136 PBIO_OS_ASYNC_BEGIN (state );
180137
181138 // Can only write one thing at once.
@@ -195,13 +152,19 @@ pbio_error_t pbdrv_uart_write(pbio_os_state_t *state, pbdrv_uart_dev_t *uart, ui
195152 PBIO_OS_AWAIT_UNTIL (state , ({
196153 // Try to write one byte if any are remaining.
197154 if (uart -> write_pos < uart -> write_length ) {
198- if (pbdrv_uart_try_to_write_byte (uart , uart -> write_buf [uart -> write_pos ])) {
155+ // Attempt to write one byte with the PRU UART. We could be sending
156+ // more than one byte at a time, but we keep it consistent with the
157+ // current PRU API. EV3 sensors don't send much data anyway.
158+ if (pbdrv_uart_ev3_pru_write_bytes (pdata -> peripheral_id , & uart -> write_buf [uart -> write_pos ], 1 )) {
199159 uart -> write_pos ++ ;
200160 }
201161 }
162+ // Completion on transmission of whole message and finishing writing, or timeout.
163+ bool complete = pbdrv_uart_ev3_pru_can_write (pdata -> peripheral_id ) && uart -> write_pos == uart -> write_length ;
164+ bool expired = timeout && pbio_os_timer_is_expired (& uart -> write_timer );
202165
203- // Completion on transmission of whole message and finishing writing or timeout .
204- ( pbdrv_uart_can_write ( uart ) && uart -> write_pos == uart -> write_length ) || ( timeout && pbio_os_timer_is_expired ( & uart -> write_timer )) ;
166+ // Await until complete or timed out .
167+ complete || expired ;
205168 }));
206169
207170 uart -> write_buf = NULL ;
@@ -213,6 +176,76 @@ pbio_error_t pbdrv_uart_write(pbio_os_state_t *state, pbdrv_uart_dev_t *uart, ui
213176 PBIO_OS_ASYNC_END (PBIO_SUCCESS );
214177}
215178
179+ pbio_error_t pbdrv_uart_write_hw (pbio_os_state_t * state , pbdrv_uart_dev_t * uart , uint8_t * msg , uint8_t length , uint32_t timeout ) {
180+
181+ const pbdrv_uart_ev3_platform_data_t * pdata = uart -> pdata ;
182+
183+ PBIO_OS_ASYNC_BEGIN (state );
184+
185+ // Can only write one thing at once.
186+ if (uart -> write_buf ) {
187+ return PBIO_ERROR_BUSY ;
188+ }
189+
190+ uart -> write_buf = msg ;
191+
192+ // Write length and pos properties not used in this implementation.
193+ uart -> write_length = 0 ;
194+ uart -> write_pos = 0 ;
195+
196+ if (timeout ) {
197+ pbio_os_timer_set (& uart -> write_timer , timeout );
198+ }
199+
200+ volatile EDMA3CCPaRAMEntry paramSet = {
201+ .srcAddr = (uint32_t )uart -> write_buf ,
202+ // UART transmit register address.
203+ .destAddr = pdata -> base_address + UART_THR ,
204+ // Number of bytes in an array.
205+ .aCnt = 1 ,
206+ // Number of such arrays to be transferred.
207+ .bCnt = length ,
208+ // Number of frames of aCnt*bBcnt bytes to be transferred.
209+ .cCnt = 1 ,
210+ // The src index should increment for every byte being transferred.
211+ .srcBIdx = 1 ,
212+ // The dst index should not increment since it is a h/w register.
213+ .destBIdx = 0 ,
214+ // Transfer mode.
215+ .srcCIdx = 0 ,
216+ .destCIdx = 0 ,
217+ .linkAddr = 0xFFFF ,
218+ .bCntReload = 0 ,
219+ .opt = EDMA3CC_OPT_DAM | ((pdata -> sys_int_uart_tx_int_id << EDMA3CC_OPT_TCC_SHIFT ) & EDMA3CC_OPT_TCC ) | (1 << EDMA3CC_OPT_TCINTEN_SHIFT ),
220+ };
221+
222+ // Save configuration and start transfer.
223+ EDMA3SetPaRAM (SOC_EDMA30CC_0_REGS , pdata -> sys_int_uart_tx_int_id , (EDMA3CCPaRAMEntry * )& paramSet );
224+ EDMA3EnableTransfer (SOC_EDMA30CC_0_REGS , pdata -> sys_int_uart_tx_int_id , EDMA3_TRIG_MODE_EVENT );
225+ UARTDMAEnable (pdata -> base_address , UART_RX_TRIG_LEVEL_1 | UART_DMAMODE | UART_FIFO_MODE );
226+
227+ // Await until all bytes are written or timeout reached.
228+ PBIO_OS_AWAIT_UNTIL (state , !uart -> write_buf || (timeout && pbio_os_timer_is_expired (& uart -> write_timer )));
229+
230+ uart -> write_buf = NULL ;
231+
232+ if (timeout && pbio_os_timer_is_expired (& uart -> write_timer )) {
233+ return PBIO_ERROR_TIMEDOUT ;
234+ }
235+
236+ PBIO_OS_ASYNC_END (PBIO_SUCCESS );
237+ }
238+
239+ pbio_error_t pbdrv_uart_write (pbio_os_state_t * state , pbdrv_uart_dev_t * uart , uint8_t * msg , uint8_t length , uint32_t timeout ) {
240+ const pbdrv_uart_ev3_platform_data_t * pdata = uart -> pdata ;
241+
242+ if (pdata -> uart_kind == EV3_UART_HW ) {
243+ return pbdrv_uart_write_hw (state , uart , msg , length , timeout );
244+ } else {
245+ return pbdrv_uart_write_pru (state , uart , msg , length , timeout );
246+ }
247+ }
248+
216249void pbdrv_uart_set_baud_rate (pbdrv_uart_dev_t * uart , uint32_t baud ) {
217250 if (uart -> pdata -> uart_kind == EV3_UART_HW ) {
218251 UARTConfigSetExpClk (uart -> pdata -> base_address , SOC_UART_0_MODULE_FREQ , baud , UART_WORDL_8BITS , UART_OVER_SAMP_RATE_16 );
@@ -238,13 +271,18 @@ void pbdrv_uart_flush(pbdrv_uart_dev_t *uart) {
238271 }
239272}
240273
274+ /**
275+ * Handles RX interrupts for the hardware UART.
276+ *
277+ * @param [in] uart The UART device.
278+ */
241279void pbdrv_uart_ev3_hw_handle_irq (pbdrv_uart_dev_t * uart ) {
242280
243281 /* This determines the cause of UART0 interrupt.*/
244282 unsigned int int_id = UARTIntStatus (uart -> pdata -> base_address );
245283
246284 /* Clears the system interrupt status of UART in AINTC. */
247- IntSystemStatusClear (uart -> pdata -> sys_int_uart_int_id );
285+ IntSystemStatusClear (uart -> pdata -> sys_int_uart_rx_int_id );
248286
249287 /* Check if the cause is receiver data condition.*/
250288 if (UART_INTID_RX_DATA == (int_id & UART_INTID_RX_DATA ) || (UART_INTID_CTI == (int_id & UART_INTID_CTI ))) {
@@ -272,7 +310,8 @@ void pbdrv_uart_ev3_hw_handle_irq(pbdrv_uart_dev_t *uart) {
272310
273311void pbdrv_uart_ev3_pru_handle_irq (pbdrv_uart_dev_t * uart ) {
274312
275- IntSystemStatusClear (uart -> pdata -> sys_int_uart_int_id );
313+ // rx and tx have the same interrupt ID.
314+ IntSystemStatusClear (uart -> pdata -> sys_int_uart_rx_int_id );
276315
277316 // This calls what is mostly equivalent the original LEGO/ev3dev IRQ
278317 // handler. This is using its own ring buffer that we pull data from below.
@@ -295,34 +334,41 @@ void pbdrv_uart_ev3_handle_irq(uint8_t id) {
295334 }
296335}
297336
337+ void pbdrv_uart_ev3_handle_tx_complete (uint8_t id ) {
338+ pbdrv_uart_dev_t * uart = & uart_devs [id ];
339+ UARTDMADisable (uart -> pdata -> base_address , (UART_RX_TRIG_LEVEL_1 | UART_FIFO_MODE ));
340+ uart -> write_buf = NULL ;
341+ pbio_os_request_poll ();
342+ }
343+
298344static void pbdrv_uart_init_hw (pbdrv_uart_dev_t * uart ) {
299345 const pbdrv_uart_ev3_platform_data_t * pdata = uart -> pdata ;
300346
301- /* Enabling the PSC for given UART.*/
347+ // Enabling the PSC for given UART.
302348 PSCModuleControl (SOC_PSC_1_REGS , pdata -> peripheral_id , PSC_POWERDOMAIN_ALWAYS_ON , PSC_MDCTL_NEXT_ENABLE );
303349
304- /* Enabling the transmitter and receiver*/
350+ // Enabling the transmitter and receiver.
305351 UARTEnable (pdata -> base_address );
306352
307- /* Configuring the UART clock and baud parameters*/
353+ // Configuring the UART clock and baud parameters.
308354 pbdrv_uart_set_baud_rate (uart , BAUD_115200 );
309355
310- /* Enabling the FIFO and flushing the Tx and Rx FIFOs.*/
356+ // Enabling the FIFO and flushing the Tx and Rx FIFOs.
311357 UARTFIFOEnable (pdata -> base_address );
312358
313- /* Setting the UART Receiver Trigger Level*/
359+ // Setting the UART Receiver Trigger Level.
314360 UARTFIFOLevelSet (pdata -> base_address , UART_RX_TRIG_LEVEL_1 );
315361
316- /* Registers the UARTIsr in the Interrupt Vector Table of AINTC. */
317- IntRegister (pdata -> sys_int_uart_int_id , pdata -> isr_handler );
318-
319- /* Map the channel number 2 of AINTC to UART system interrupt. */
320- IntChannelSet (pdata -> sys_int_uart_int_id , 2 );
321-
322- IntSystemEnable (pdata -> sys_int_uart_int_id );
323-
324- /* Enable the Interrupts in UART.*/
362+ // Registers the UARTIsr for reading in the Interrupt Vector Table of AINTC.
363+ // Map the channel number 2 of AINTC to UART system interrupt.
364+ // Interrupts for reading stay enabled, unlike writing.
365+ IntRegister (pdata -> sys_int_uart_rx_int_id , pdata -> isr_handler );
366+ IntChannelSet (pdata -> sys_int_uart_rx_int_id , 2 );
367+ IntSystemEnable (pdata -> sys_int_uart_rx_int_id );
325368 UARTIntEnable (pdata -> base_address , UART_INT_LINE_STAT | UART_INT_RXDATA_CTI );
369+
370+ // Request DMA Channel for UART Transmit at event queue 0.
371+ EDMA3RequestChannel (SOC_EDMA30CC_0_REGS , EDMA3_CHANNEL_TYPE_DMA , pdata -> sys_int_uart_tx_int_id , pdata -> sys_int_uart_tx_int_id , 0 );
326372}
327373
328374
@@ -333,12 +379,11 @@ void pbdrv_uart_init_pru(pbdrv_uart_dev_t *uart) {
333379 pbdrv_uart_set_baud_rate (uart , BAUD_115200 );
334380
335381 // Registers the UARTIsr in the Interrupt Vector Table of AINTC.
336- IntRegister (pdata -> sys_int_uart_int_id , pdata -> isr_handler );
337-
338382 // Map the channel number 2 of AINTC to UART system interrupt.
339- IntChannelSet (pdata -> sys_int_uart_int_id , 2 );
340-
341- IntSystemEnable (pdata -> sys_int_uart_int_id );
383+ // One handler for both RX and TX interrupts with same ID.
384+ IntRegister (pdata -> sys_int_uart_tx_int_id , pdata -> isr_handler );
385+ IntChannelSet (pdata -> sys_int_uart_tx_int_id , 2 );
386+ IntSystemEnable (pdata -> sys_int_uart_tx_int_id );
342387}
343388
344389void pbdrv_uart_init (void ) {
0 commit comments