Changeset 630:6ccb3e5f852d
- Timestamp:
- 08/15/08 22:59:44 (3 months ago)
- Author:
- David Anderson <dave@…>
- Branch:
- default
- Tags:
- tip
- Message:
-
Make the RS485 driver use the DMA controller for data transfers.
This change simplifies the driver a little, but more importantly results in
a single interrupt for a whole transfer, instead of one interrupt per byte
in PIO mode.
- Files:
-
Legend:
- Unmodified
- Added
- Removed
-
|
r629
|
r630
|
|
| 22 | 22 | |
| 23 | 23 | static volatile struct { |
| 24 | | U8 *buffer; /* Pointer to the buffer */ |
| 25 | | U32 buflen; /* Buffer total length */ |
| 26 | 24 | enum { |
| 27 | 25 | RS485_UNINITIALIZED = 0, |
| … |
… |
|
| 83 | 81 | AT91C_PIO_PA7; |
| 84 | 82 | |
| 85 | | static inline void isr_finish(void) { |
| 86 | | rs485_state.buffer++; |
| 87 | | rs485_state.buflen--; |
| 88 | | if (rs485_state.buflen <= 0) { |
| 89 | | *AT91C_US0_IDR = AT91C_US_RXRDY | AT91C_US_TXRDY; |
| | 83 | static void nx_rs485_isr(void) { |
| | 84 | const U32 csr = *AT91C_US0_CSR; |
| | 85 | |
| | 86 | /* Successful transfer completion first, then error cases. */ |
| | 87 | if ((rs485_state.status == RS485_RECEIVING && (csr & AT91C_US_ENDRX)) || |
| | 88 | (rs485_state.status == RS485_TRANSMITTING && (csr & AT91C_US_ENDTX))) { |
| | 89 | *AT91C_US0_IDR = AT91C_US_ENDRX | AT91C_US_ENDTX; |
| 90 | 90 | *AT91C_US0_CR = AT91C_US_RXDIS | AT91C_US_TXDIS; |
| 91 | 91 | rs485_state.status = RS485_IDLE; |
| 92 | | if (rs485_state.callback) { |
| | 92 | |
| | 93 | if (rs485_state.callback) |
| 93 | 94 | rs485_state.callback(); |
| 94 | | } |
| | 95 | } else if (csr & AT91C_US_TIMEOUT) { |
| | 96 | /* TODO: handle timeouts */ |
| | 97 | } else if (csr & AT91C_US_OVRE) { |
| | 98 | /* TODO: handle overruns */ |
| | 99 | } else if (csr & AT91C_US_FRAME) { |
| | 100 | /* TODO: handle framing errors */ |
| 95 | 101 | } |
| 96 | 102 | } |
| 97 | 103 | |
| 98 | | static void nx_rs485_isr(void) { |
| 99 | | const U32 csr = *AT91C_US0_CSR; |
| 100 | | |
| 101 | | /* Discern between interrupt types, without cleaning the CSR */ |
| 102 | | if (csr & AT91C_US_TIMEOUT) { |
| 103 | | /* TODO manage timeouts */ |
| 104 | | } else if (csr & AT91C_US_OVRE) { |
| 105 | | /* Overrun error, TODO signal it */ |
| 106 | | *AT91C_US0_CR = AT91C_US_RSTSTA; |
| 107 | | } else if (csr & AT91C_US_RXRDY && rs485_state.status == RS485_RECEIVING) { |
| 108 | | if (csr & AT91C_US_FRAME) { |
| 109 | | /* Framing error, TODO signal it */ |
| 110 | | return; |
| 111 | | } |
| 112 | | *rs485_state.buffer = *AT91C_US0_RHR; |
| 113 | | isr_finish(); |
| 114 | | } else if (csr & AT91C_US_TXRDY && rs485_state.status == RS485_TRANSMITTING) { |
| 115 | | *AT91C_US0_THR = *rs485_state.buffer; |
| 116 | | isr_finish(); |
| 117 | | } |
| 118 | | } |
| 119 | | |
| 120 | | static inline void datainit(U8 *buffer, U32 buflen, nx_closure_t callback) { |
| 121 | | rs485_state.buffer = buffer; |
| 122 | | rs485_state.buflen = buflen; |
| 123 | | rs485_state.callback = callback; |
| 124 | | } |
| 125 | | |
| 126 | 104 | void nx_rs485_init(void) { |
| 127 | | /* Avoid double initialization */ |
| 128 | 105 | NX_ASSERT(rs485_state.status == RS485_UNINITIALIZED); |
| 129 | | |
| 130 | | /* Install isr */ |
| 131 | | /* FIXME: move isr installation at the end of init function */ |
| 132 | | nx_interrupts_disable(); |
| 133 | | nx_aic_install_isr(AT91C_ID_US0, AIC_PRIO_DRIVER, AIC_TRIG_LEVEL, |
| 134 | | nx_rs485_isr); |
| 135 | 106 | |
| 136 | 107 | /* Enable the peripheral clock */ |
| … |
… |
|
| 143 | 114 | |
| 144 | 115 | /* Reset and disable to avoid funny jokes: */ |
| 145 | | /* FIXME: try to remove RXDIS and TXDIS */ |
| 146 | 116 | *AT91C_US0_CR = AT91C_US_RSTRX | /* Transmitter reset */ |
| 147 | 117 | AT91C_US_RSTTX | /* Receiver reset */ |
| 148 | | AT91C_US_RSTSTA | /* Status bits */ |
| 149 | | AT91C_US_TXDIS | /* Transmitter disable */ |
| 150 | | AT91C_US_RXDIS; /* Receiver disable */ |
| | 118 | AT91C_US_RSTSTA; /* Status bits */ |
| 151 | 119 | |
| 152 | 120 | /* Placing the Usart settings in the appropriate register, time guard, |
| … |
… |
|
| 157 | 125 | *AT91C_US0_BRGR = build_baud_rate(nx_rs485_settings.baud_rate); |
| 158 | 126 | |
| 159 | | /* Enable USART interrupts (keeping transmitter and receiver disabled, |
| 160 | | * since they will be enabled on the specific operation |
| | 127 | /* Enable USART interrupts for error conditions. Normal transmission |
| | 128 | * conditions are enabled only when transfers are initiated. |
| 161 | 129 | */ |
| 162 | | *AT91C_US0_IER = AT91C_US_OVRE | /* Overrun error (overwritten data) */ |
| 163 | | AT91C_US_FRAME | /* Framing error (temporization) */ |
| 164 | | AT91C_US_TXEMPTY; /* All data transmitted. */ |
| 165 | | /* TODO: check interrupt triggering here */ |
| 166 | | |
| 167 | | *AT91C_US0_IDR = AT91C_US_RXRDY | /* Receiver ready */ |
| 168 | | AT91C_US_TXRDY; /* Transmitter ready */ |
| | 130 | *AT91C_US0_IDR = ~0; |
| | 131 | *AT91C_US0_IER = AT91C_US_OVRE | /* Overrun error (overwritten data) */ |
| | 132 | AT91C_US_FRAME; /* Framing error */ |
| 169 | 133 | |
| 170 | 134 | if (nx_rs485_settings.timeout > 0) { |
| 171 | 135 | *AT91C_US0_IER = AT91C_US_TIMEOUT; /* Timeout */ |
| 172 | | /* TODO: signal it */ |
| 173 | 136 | } |
| 174 | 137 | |
| 175 | | /* Install interrupt service routine and reenable interrupt handling */ |
| 176 | | nx_interrupts_enable(); |
| | 138 | /* Install the rs485 ISR. */ |
| | 139 | nx_aic_install_isr(AT91C_ID_US0, AIC_PRIO_DRIVER, AIC_TRIG_LEVEL, |
| | 140 | nx_rs485_isr); |
| 177 | 141 | |
| 178 | 142 | rs485_state.status = RS485_IDLE; |
| … |
… |
|
| 197 | 161 | if (rs485_state.status != RS485_IDLE) |
| 198 | 162 | return FALSE; |
| | 163 | |
| 199 | 164 | rs485_state.status = RS485_TRANSMITTING; |
| 200 | | datainit(buffer, buflen, callback); |
| 201 | | |
| 202 | | /* Enable the transmitter */ |
| 203 | | *AT91C_US0_IER = AT91C_US_TXRDY; |
| | 165 | rs485_state.callback = callback; |
| | 166 | |
| | 167 | /* Set up a DMA write and start transferring asynchronously. */ |
| | 168 | *AT91C_US0_TPR = (U32)buffer; |
| | 169 | *AT91C_US0_TCR = buflen; |
| | 170 | *AT91C_US0_IER = AT91C_US_ENDTX; |
| | 171 | *AT91C_US0_PTCR = AT91C_PDC_TXTEN; |
| 204 | 172 | *AT91C_US0_CR = AT91C_US_TXEN; |
| 205 | 173 | |
| … |
… |
|
| 212 | 180 | if (rs485_state.status != RS485_IDLE) |
| 213 | 181 | return FALSE; |
| | 182 | |
| 214 | 183 | rs485_state.status = RS485_RECEIVING; |
| 215 | | datainit(buffer, buflen, callback); |
| 216 | | |
| 217 | | /* Enable the receiver */ |
| 218 | | *AT91C_US0_IER = AT91C_US_RXRDY; |
| | 184 | rs485_state.callback = callback; |
| | 185 | |
| | 186 | /* Set up a DMA read and start transferring asynchronously. */ |
| | 187 | *AT91C_US0_RPR = (U32)buffer; |
| | 188 | *AT91C_US0_RCR = buflen; |
| | 189 | *AT91C_US0_IER = AT91C_US_ENDRX; |
| | 190 | *AT91C_US0_PTCR = AT91C_PDC_RXTEN; |
| | 191 | |
| | 192 | /* Due to (apparently) the wiring of the RS485 bus, both the |
| | 193 | * transmitter and the receiver have to be enabled, otherwise the |
| | 194 | * receiver does not receive any data. |
| | 195 | * |
| | 196 | * TODO: Figure out the exact reason and document it. |
| | 197 | */ |
| 219 | 198 | *AT91C_US0_CR = AT91C_US_RXEN | AT91C_US_TXEN; |
| 220 | 199 | |
| 221 | 200 | return TRUE; |
| 222 | 201 | } |
| 223 | | |