Show
Ignore:
Timestamp:
08/15/08 22:59:44 (5 months ago)
Author:
David Anderson <dave@…>
Branch:
default
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:
1 modified

Legend:

Unmodified
Added
Removed
  • nxos/base/drivers/rs485.c

    r629 r630  
    2222 
    2323static volatile struct { 
    24   U8 *buffer;           /* Pointer to the buffer */ 
    25   U32 buflen;           /* Buffer total length */ 
    2624  enum { 
    2725    RS485_UNINITIALIZED = 0, 
     
    8381                                        AT91C_PIO_PA7; 
    8482 
    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; 
     83static 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; 
    9090    *AT91C_US0_CR = AT91C_US_RXDIS | AT91C_US_TXDIS; 
    9191    rs485_state.status = RS485_IDLE; 
    92     if (rs485_state.callback) { 
     92 
     93    if (rs485_state.callback) 
    9394      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 */ 
    95101  } 
    96102} 
    97103 
    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  
    126104void nx_rs485_init(void) { 
    127   /* Avoid double initialization */ 
    128105  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); 
    135106 
    136107  /* Enable the peripheral clock */ 
     
    143114 
    144115  /* Reset and disable to avoid funny jokes: */ 
    145   /* FIXME: try to remove RXDIS and TXDIS */ 
    146116  *AT91C_US0_CR = AT91C_US_RSTRX  |  /* Transmitter reset */ 
    147117                  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 */ 
    151119 
    152120  /* Placing the Usart settings in the appropriate register, time guard, 
     
    157125  *AT91C_US0_BRGR = build_baud_rate(nx_rs485_settings.baud_rate); 
    158126 
    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. 
    161129   */ 
    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 */ 
    169133 
    170134  if (nx_rs485_settings.timeout > 0) { 
    171135    *AT91C_US0_IER = AT91C_US_TIMEOUT; /* Timeout */ 
    172     /* TODO: signal it */ 
    173136  } 
    174137 
    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); 
    177141 
    178142  rs485_state.status = RS485_IDLE; 
     
    197161  if (rs485_state.status != RS485_IDLE) 
    198162    return FALSE; 
     163 
    199164  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; 
    204172  *AT91C_US0_CR = AT91C_US_TXEN; 
    205173 
     
    212180  if (rs485_state.status != RS485_IDLE) 
    213181    return FALSE; 
     182 
    214183  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   */ 
    219198  *AT91C_US0_CR = AT91C_US_RXEN | AT91C_US_TXEN; 
    220199 
    221200  return TRUE; 
    222201} 
    223