MAX3100, an external serial device to add additional serial ports via SPI

Dependents:   FLIGHT_CONTROL_AND_COMMUNICATIONS_SYSTEM

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MAX3100.cpp Source File

MAX3100.cpp

00001 /*
00002     Copyright (c) 2011 Andy Kirkham
00003  
00004     Permission is hereby granted, free of charge, to any person obtaining a copy
00005     of this software and associated documentation files (the "Software"), to deal
00006     in the Software without restriction, including without limitation the rights
00007     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008     copies of the Software, and to permit persons to whom the Software is
00009     furnished to do so, subject to the following conditions:
00010  
00011     The above copyright notice and this permission notice shall be included in
00012     all copies or substantial portions of the Software.
00013  
00014     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00019     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00020     THE SOFTWARE.
00021 */
00022 
00023 #include "MAX3100.h"
00024 
00025 namespace AjK {
00026 
00027 void 
00028 MAX3100::init(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName irq, SPI *spi) 
00029 {
00030     config = 0;
00031     flushTxBuffer();
00032     flushRxBuffer();
00033     
00034     _parity = 0;
00035     
00036     _cs_function = NULL;
00037     _cs_obj      = NULL;
00038     _cs_method   = NULL;
00039     
00040     _isr_user_function = NULL;
00041     _isr_user_obj      = NULL;
00042     _isr_user_method   = NULL;
00043     
00044     if (cs != NC) {    
00045         _cs = new DigitalOut(cs);
00046         _cs->write( 1 );
00047     }
00048     else _cs = (DigitalOut *)NULL;
00049                 
00050     if (spi) {
00051         _spi = spi;
00052     }
00053     else {                
00054         _spi = new SPI(mosi, miso, sclk);
00055         _spi->format(16, 0);
00056         _spi->frequency(MAX3100_SPI_FREQ);
00057     }
00058     
00059     if (irq != NC) {
00060         irqMask(irq);
00061         _irq = new InterruptIn(irq);
00062         _irq->mode(PullUp);
00063         topic_1498(irq);       
00064         _irq->fall(this, &MAX3100::isr);
00065     }
00066     else { _irq = (InterruptIn *)NULL; }
00067         
00068     baud(10); // 9600baud by default.
00069 }
00070 
00071 void 
00072 MAX3100::cs_value(int i)
00073 {
00074     if (_cs != (DigitalOut *)NULL) _cs->write(i & 1); 
00075     else {
00076         if (_cs_function != NULL) (*_cs_function)(_device, i & 1);
00077         else {
00078             if (_cs_obj && _cs_method) (_cs_obj->*_cs_method)(_device, i & 1);
00079         }
00080     }
00081 }
00082     
00083 uint16_t 
00084 MAX3100::spiwrite(uint16_t val) 
00085 {
00086     cs_value(0);
00087     uint16_t r = _spi->write(val);
00088     cs_value(1);
00089     return r;
00090 }
00091 
00092 uint16_t 
00093 MAX3100::config_write(uint16_t val) 
00094 {
00095     return spiwrite(MAX3100_CONF_WR | val); 
00096 }
00097 
00098 uint16_t 
00099 MAX3100::config_read(void)          
00100 { 
00101     return spiwrite(MAX3100_CONF_RD);     
00102 }
00103 
00104 
00105 void 
00106 MAX3100::baud(int baudrate) 
00107 {        
00108     irqDisable();
00109     config &= ~(0xf);
00110     config |= (baudrate & 0xf);
00111     config_write(config);
00112     irqEnable();
00113 }
00114     
00115 void 
00116 MAX3100::enableRxIrq(void) 
00117 { 
00118     irqDisable();
00119     config &= ~MAX3100_RM(1);
00120     config |=  MAX3100_RM(1);
00121     config_write(config); 
00122     irqEnable();
00123 }
00124     
00125 void 
00126 MAX3100::disableRxIrq(void) 
00127 { 
00128     irqDisable();
00129     config &= ~MAX3100_RM(1);
00130     config_write(config);     
00131     irqEnable();
00132 }
00133     
00134 void 
00135 MAX3100::enableTxIrq(void) 
00136 { 
00137     irqDisable();
00138     config &= ~MAX3100_TM(1);
00139     config |=  MAX3100_TM(1);
00140     config_write(config); 
00141     irqEnable();
00142 }
00143     
00144 void 
00145 MAX3100::disableTxIrq(void) 
00146 { 
00147     irqDisable();
00148     config &= ~MAX3100_TM(1);
00149     config_write(config);     
00150     irqEnable();
00151 }
00152         
00153 int 
00154 MAX3100::putc(int c) 
00155 { 
00156     uint16_t data, conf;
00157 
00158     // If no space return -1 as an error code.    
00159     if (tx_buffer_full) return -1;
00160 
00161     if (_parity) {
00162         int pBit = parityCal(c & 0xFF);        
00163         if (_parity == Even && pBit == 0) { c |= (1 << 8); }
00164         if (_parity == Odd  && pBit == 1) { c |= (1 << 8); }
00165     }
00166     else { c &= 0xFF; }
00167     
00168     // Function is non-interruptable by the MAX3100 class
00169     // to avoid SPI bus contention between writing a byte
00170     // in user context (here) and IRQ context.    
00171     irqDisable();
00172 
00173     conf = config_read();
00174         
00175     if (tx_buffer_in == tx_buffer_out && conf & MAX3100_CONF_T) {
00176         data = spiwrite(MAX3100_DATA_WR | (c & 0x1FF));              
00177         // In case we get a byte while writing store it away.
00178         if (!rx_buffer_full && data & MAX3100_CONF_R) {
00179             rx_buffer[rx_buffer_in++] = (uint16_t)(data & 0xFF);
00180             if (rx_buffer_in >= MAX3100_RX_BUFFER_SIZE) rx_buffer_in = 0;
00181             if (rx_buffer_in == rx_buffer_out) rx_buffer_full = true;
00182         }
00183     }
00184     else {
00185         tx_buffer[tx_buffer_in++] = (char)(c & 0xFF);
00186         if (tx_buffer_in >= MAX3100_TX_BUFFER_SIZE) {
00187             tx_buffer_in = 0;
00188         }
00189         if (tx_buffer_in == tx_buffer_out) tx_buffer_full = true;
00190     }
00191     
00192     irqEnable();
00193         
00194     return 1;
00195 }
00196 
00197 void
00198 MAX3100::puts(char *s) {
00199     char *q = s;
00200     while(*(q)) {
00201         if (putc((int)(*(q))) == -1) return;
00202         q++;
00203     }
00204 }
00205 
00206 int 
00207 MAX3100::getc(void) { 
00208     if (!rx_buffer_full && rx_buffer_in == rx_buffer_out) return -1;
00209     int c = (int)((unsigned char)rx_buffer[rx_buffer_out++]);
00210     if (rx_buffer_out >= MAX3100_RX_BUFFER_SIZE) rx_buffer_out = 0;
00211     rx_buffer_full = false;
00212     return c;
00213 }
00214 
00215 char *
00216 MAX3100::gets(char *s, int size)
00217 {
00218     int i;
00219     char *q = s;
00220     while(size) { 
00221         do { i = getc(); } while (i == -1); // Blocks!
00222         *(q) = (char)i; size--; 
00223     }
00224     return s; 
00225 }
00226 
00227 int 
00228 MAX3100::peek(void) { 
00229     if (!rx_buffer_full && rx_buffer_in == rx_buffer_out) return -1;
00230     return (int)((unsigned char)rx_buffer[rx_buffer_out]);
00231 }
00232 
00233 void 
00234 MAX3100::isr(void) {
00235     uint16_t data = spiwrite(MAX3100_DATA_RD);
00236     bool tx_ready = data & MAX3100_CONF_T ? true : false;
00237         
00238     // The MAX3100 does have an RX fifo. So attempt to empty it into the RX buffer.
00239     do {
00240         if (!rx_buffer_full && data & MAX3100_CONF_R) {
00241             rx_buffer[rx_buffer_in++] = (char)(data & 0xFF);
00242             if (rx_buffer_in >= MAX3100_RX_BUFFER_SIZE) rx_buffer_in = 0;
00243             if (rx_buffer_in == rx_buffer_out) rx_buffer_full = true;
00244         }
00245     }
00246     while ((data = spiwrite(MAX3100_DATA_RD)) & MAX3100_CONF_R);
00247         
00248     // The MAX3100 doesn't have a hardware TX fifo, so just test to see if it's TX buffer
00249     // is empty. If it is and we have bytes in the TX buffer then send one now.
00250     if (tx_ready && (tx_buffer_full || tx_buffer_in != tx_buffer_out)) {
00251         data = spiwrite(MAX3100_DATA_WR | (tx_buffer[tx_buffer_out++] & 0x1FF));
00252         if (tx_buffer_out >= MAX3100_TX_BUFFER_SIZE) tx_buffer_out = 0;
00253         tx_buffer_full = false;                                        
00254     }
00255         
00256     // In case we get a byte while sending then store it.
00257     if (!rx_buffer_full && data & MAX3100_CONF_R) {
00258         rx_buffer[rx_buffer_in++] = (char)(data & 0xFF);
00259         if (rx_buffer_in >= MAX3100_RX_BUFFER_SIZE) rx_buffer_in = 0;
00260         if (rx_buffer_in == rx_buffer_out) rx_buffer_full = true;
00261     }
00262     
00263     int isrType = MAX3100::ISR;     
00264     
00265     if ( data & MAX3100_CONF_R ) {
00266         isrType |= MAX3100::ISR_RX;     
00267     }
00268     
00269     if ( tx_ready ) {
00270         isrType |= MAX3100::ISR_TX;     
00271     }
00272     
00273     
00274     if (_isr_user_function != NULL) (*_isr_user_function)( isrType );
00275     else {
00276        if (_isr_user_obj && _isr_user_method) (_isr_user_obj->*_isr_user_method)( isrType );
00277     }   
00278 }
00279 
00280 void 
00281 MAX3100::setStopBits(int i) 
00282 {
00283     switch(i) {
00284         case 1: 
00285             irqDisable();
00286             config &= ~(1 << 6);
00287             config_write(config);     
00288             irqEnable();
00289             break;
00290         case 2: 
00291             irqDisable();
00292             config |= (1 << 6);
00293             config_write(config);     
00294             irqEnable();
00295             break;    
00296     }
00297 }
00298 
00299 int 
00300 MAX3100::parityCal(uint8_t c)
00301 {
00302     int count = 0;
00303     for (int mask = 1, i = 0; i < 8; i++, mask = mask << 1) {
00304         if (c & mask) count++;
00305     }
00306     return count & 1;
00307 }
00308 
00309 void
00310 MAX3100::irqDisable(void)
00311 {
00312     if (_irqMask0) LPC_GPIOINT->IO0IntEnF &= ~_irqMask0;
00313     if (_irqMask2) LPC_GPIOINT->IO2IntEnF &= ~_irqMask2;    
00314 }
00315 
00316 void
00317 MAX3100::irqEnable(void)
00318 {
00319     if (_irqMask0) LPC_GPIOINT->IO0IntEnF |= _irqMask0;
00320     if (_irqMask2) LPC_GPIOINT->IO2IntEnF |= _irqMask2;    
00321 }
00322 
00323 void 
00324 MAX3100::irqMask(PinName p)
00325 {
00326     _irqMask0 = _irqMask2 = 0;
00327     
00328     switch( p ) {
00329         case p5:  _irqMask0 = (1UL << 9); break;
00330         case p6:  _irqMask0 = (1UL << 8); break;
00331         case p7:  _irqMask0 = (1UL << 7); break;
00332         case p8:  _irqMask0 = (1UL << 6); break;
00333         case p9:  _irqMask0 = (1UL << 0); break;
00334         case p10: _irqMask0 = (1UL << 1); break;
00335         case p11: _irqMask0 = (1UL << 18); break;
00336         case p12: _irqMask0 = (1UL << 17); break;
00337         case p13: _irqMask0 = (1UL << 15); break;
00338         case p14: _irqMask0 = (1UL << 16); break;
00339         case p15: _irqMask0 = (1UL << 23); break;
00340         case p16: _irqMask0 = (1UL << 24); break;
00341         case p17: _irqMask0 = (1UL << 25); break;
00342         case p18: _irqMask0 = (1UL << 26); break;
00343         case p21: _irqMask2 = (1UL << 5); break;
00344         case p22: _irqMask2 = (1UL << 4); break;
00345         case p23: _irqMask2 = (1UL << 3); break;
00346         case p24: _irqMask2 = (1UL << 2); break;
00347         case p25: _irqMask2 = (1UL << 1); break;
00348         case p26: _irqMask2 = (1UL << 0); break;
00349         case p27: _irqMask0 = (1UL << 11); break;
00350         case p28: _irqMask0 = (1UL << 10); break;
00351         case p29: _irqMask0 = (1UL << 5); break;
00352         case p30: _irqMask0 = (1UL << 4); break;        
00353     }
00354 }
00355         
00356 void 
00357 MAX3100::topic_1498(PinName p) {
00358     // http://mbed.org/forum/bugs-suggestions/topic/1498
00359     uint32_t clr0 = 0, clr2 = 0;
00360         
00361     switch( p ) {
00362         case p5:  clr0 = (1UL << 9); break;
00363         case p6:  clr0 = (1UL << 8); break;
00364         case p7:  clr0 = (1UL << 7); break;
00365         case p8:  clr0 = (1UL << 6); break;
00366         case p9:  clr0 = (1UL << 0); break;
00367         case p10: clr0 = (1UL << 1); break;
00368         case p11: clr0 = (1UL << 18); break;
00369         case p12: clr0 = (1UL << 17); break;
00370         case p13: clr0 = (1UL << 15); break;
00371         case p14: clr0 = (1UL << 16); break;
00372         case p15: clr0 = (1UL << 23); break;
00373         case p16: clr0 = (1UL << 24); break;
00374         case p17: clr0 = (1UL << 25); break;
00375         case p18: clr0 = (1UL << 26); break;
00376         case p21: clr2 = (1UL << 5); break;
00377         case p22: clr2 = (1UL << 4); break;
00378         case p23: clr2 = (1UL << 3); break;
00379         case p24: clr2 = (1UL << 2); break;
00380         case p25: clr2 = (1UL << 1); break;
00381         case p26: clr2 = (1UL << 0); break;
00382         case p27: clr0 = (1UL << 11); break;
00383         case p28: clr0 = (1UL << 10); break;
00384         case p29: clr0 = (1UL << 5); break;
00385         case p30: clr0 = (1UL << 4); break;        
00386     }
00387         
00388     if (clr0) LPC_GPIOINT->IO0IntClr = clr0;
00389     if (clr2) LPC_GPIOINT->IO2IntClr = clr2;            
00390 }
00391 
00392 }; // namespace AjK ends