Adapted to work with the LPC4088 Experiment Base Board

Dependents:   lpc4088_ebb_ptp Algorithm-testing

Fork of I2S by Giles Barton-Owen

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers I2S.cpp Source File

I2S.cpp

00001 #include "I2S.h"
00002 
00003 #define I2S_DF_WORDWIDTH                16
00004 #define I2S_DF_SAMPLERATE               48000
00005 #define I2S_DF_MASTERSLAVE              I2S_SLAVE
00006 #define I2S_DF_STEREOMONO               I2S_STEREO
00007 #define I2S_DF_MUTED                    I2S_UNMUTED
00008 #define I2S_DF_INTERRUPT_FIFO_LEVEL     4
00009 
00010 #define I2S_MAX_DENOMINATOR             256
00011 #define I2S_MAX_NUMERATOR               256
00012 #define I2S_MAX_BITRATE_DIV             64
00013 
00014 //#define I2S_PCLK_RATE                   24000000
00015 #define I2S_PCLK_RATE                   120000000
00016 
00017 FunctionPointer I2S::I2STXISR;
00018 FunctionPointer I2S::I2SRXISR;
00019 
00020 bool I2S::txisr;
00021 bool I2S::rxisr;
00022 
00023 I2S::I2S(bool rxtx, PinName sd, PinName ws, PinName clk)
00024 {
00025     NVIC_DisableIRQ (I2S_IRQn);
00026 
00027     _sd = sd;
00028     _ws = ws;
00029     _clk = clk;
00030     _rxtx = rxtx;
00031 
00032     ws_d = true;
00033     clk_d = true;
00034     mclk_d = false;
00035 
00036     fourwire = false;
00037 
00038     reg_write_err = 0;
00039 
00040     pin_setup();
00041 
00042     if (pin_setup_err != 0) {
00043         perror("I2S Pins incorrectly defined.");
00044     }
00045 
00046     defaulter();
00047 }
00048 
00049 I2S::I2S(bool rxtx, PinName sd)
00050 {
00051     NVIC_DisableIRQ (I2S_IRQn);
00052 
00053     _sd = sd;
00054     _rxtx = rxtx;
00055 
00056     ws_d = false;
00057     clk_d = false;
00058     mclk_d = false;
00059 
00060     fourwire = false;
00061 
00062     reg_write_err = 0;
00063 
00064     pin_setup();
00065 
00066     if (pin_setup_err != 0) {
00067         perror("I2S Pins incorrectly defined.");
00068     }
00069 
00070     defaulter();
00071 }
00072 
00073 I2S::I2S(bool rxtx, PinName sd, bool fourwiremode)
00074 {
00075     NVIC_DisableIRQ (I2S_IRQn);
00076 
00077     _sd = sd;
00078     _rxtx = rxtx;
00079 
00080     ws_d = false;
00081     clk_d = false;
00082     mclk_d = false;
00083 
00084     reg_write_err = 0;
00085 
00086     fourwire = fourwiremode;
00087 
00088     pin_setup();
00089 
00090     if (pin_setup_err != 0) {
00091         perror("I2S Pins incorrectly defined.");
00092     }
00093 
00094     defaulter();
00095 }
00096 
00097 I2S::I2S(bool rxtx, PinName sd, PinName ws, bool fourwiremode)
00098 {
00099     NVIC_DisableIRQ (I2S_IRQn);
00100 
00101     _sd = sd;
00102     _ws = ws;
00103     _rxtx = rxtx;
00104 
00105     ws_d = true;
00106     clk_d = false;
00107     mclk_d = false;
00108 
00109     reg_write_err = 0;
00110 
00111     fourwire = fourwiremode;
00112 
00113     pin_setup();
00114 
00115     if (pin_setup_err != 0) {
00116         perror("I2S Pins incorrectly defined.");
00117     }
00118 
00119     defaulter();
00120 }
00121 
00122 I2S::I2S(bool rxtx, PinName sd, PinName ws)
00123 {
00124     NVIC_DisableIRQ (I2S_IRQn);
00125 
00126     _sd = sd;
00127     _ws = ws;
00128     _rxtx = rxtx;
00129 
00130     ws_d = true;
00131     clk_d = false;
00132     mclk_d = false;
00133 
00134     reg_write_err = 0;
00135 
00136     fourwire = false;
00137 
00138     pin_setup();
00139 
00140     if (pin_setup_err != 0) {
00141         perror("I2S Pins incorrectly defined.");
00142     }
00143 
00144     defaulter();
00145 }
00146 
00147 I2S::~I2S()
00148 {
00149     NVIC_DisableIRQ (I2S_IRQn);
00150     deallocating = true;
00151     pin_setup();
00152     write_registers();
00153 }
00154 
00155 void I2S::defaulter()
00156 {
00157     LPC_SC->PCONP |= (1 << 27);
00158 
00159     stop();
00160     master = false;
00161     deallocating = false;
00162 
00163     frequency(I2S_DF_SAMPLERATE);
00164     wordsize(I2S_DF_WORDWIDTH);
00165     masterslave (I2S_DF_MASTERSLAVE);
00166     stereomono (I2S_DF_STEREOMONO);
00167     set_interrupt_fifo_level(I2S_DF_INTERRUPT_FIFO_LEVEL);
00168     mute (I2S_DF_MUTED);
00169 
00170     NVIC_SetVector(I2S_IRQn, (uint32_t) & _i2sisr);
00171     NVIC_EnableIRQ (I2S_IRQn);
00172 }
00173 
00174 void I2S::write(char buf[], int len)
00175 {
00176     if (_rxtx == I2S_TRANSMIT) {
00177         if (len > max_fifo_points())
00178             len = max_fifo_points();
00179         if (len <= 0)
00180             return;
00181         int temp = 0;
00182         for (int i = 0; i < len; i += 4) {
00183             temp = 0;
00184             for (int j = 0; j < 4; j++) {
00185                 temp |= int(buf[i + j]) << (j * 8);
00186             }
00187             LPC_I2S->TXFIFO = temp;
00188         }
00189     }
00190 
00191 }
00192 
00193 void I2S::write(int buf[], int len)
00194 {
00195     if (_rxtx == I2S_TRANSMIT && wordwidth > 0) {
00196         if (len > max_fifo_points()) {
00197             len = max_fifo_points();
00198             printf("Trying to write too much data!\n\r");
00199         }
00200         if (len <= 0)
00201             return;
00202         uint32_t temp = 0;
00203         int increment = 32 / wordwidth;
00204         unsigned char recast[] =
00205         { 0, 0, 0, 0 };
00206         for (int i = 0; i < len; i += increment) {
00207             temp = 0;
00208 
00209             switch (wordwidth) {
00210 
00211                 case 8:
00212 
00213                     recast[0] = (int8_t) buf[i + 0];
00214                     recast[1] = (int8_t) buf[i + 1];
00215                     recast[2] = (int8_t) buf[i + 2];
00216                     recast[3] = (int8_t) buf[i + 3];
00217                     break;
00218                 case 16:
00219                     recast[0] = (((int16_t) buf[i + 0]) >> 0) & 0xFF;
00220                     recast[1] = (((int16_t) buf[i + 0]) >> 8) & 0xFF;
00221                     recast[2] = (((int16_t) buf[i + 1]) >> 0) & 0xFF;
00222                     recast[3] = (((int16_t) buf[i + 1]) >> 8) & 0xFF;
00223                     break;
00224                 case 32:
00225                     recast[0] = (((int32_t) buf[i + 0]) >> 0) & 0xFF;
00226                     recast[1] = (((int32_t) buf[i + 0]) >> 8) & 0xFF;
00227                     recast[2] = (((int32_t) buf[i + 0]) >> 16) & 0xFF;
00228                     recast[3] = (((int32_t) buf[i + 0]) >> 24) & 0xFF;
00229                     break;
00230 
00231             }
00232             for (int j = 0; j < 4; j++) {
00233 
00234                 temp |= recast[j] << (j * 8);
00235             }
00236 
00237             //if(((temp >> 16) & 0xFFFF) == 0xFFFF) printf("Hmmm %x %x %x\n\r",temp, increment,i); //|| temp &0xFFFF == 0xFFFF
00238             //if((buf[i]-buf[i+1])>5000 || (buf[i]-buf[i+1])<-5000) printf("J:%i,%i\n\r",buf[i],buf[i+1]);
00239             //printf("%x\n",temp);
00240             LPC_I2S->TXFIFO = temp;
00241         }
00242     }
00243 }
00244 
00245 void I2S::write(int bufr[], int bufl[], int len)
00246 {
00247     //#TODO: Write this!
00248 }
00249 
00250 int I2S::read()
00251 {
00252     return LPC_I2S->RXFIFO;
00253 }
00254 
00255 void I2S::read(char buf[], int len)
00256 {
00257     bool len_valid = true;
00258     if (len <= 0)
00259         return;
00260     if (len >= fifo_points())
00261         len = fifo_points();
00262     int temp[8];
00263     int counter = 0;
00264     int increment = 4;            //32/wordwidth;
00265     int fifo_levl = fifo_level();
00266     while (counter < fifo_levl && len_valid) {
00267         temp[counter] = LPC_I2S->RXFIFO;
00268         for (int j = 0; j < increment; j++) {
00269             if ((counter * 4) + j > len) {
00270                 len_valid = false;
00271                 break;
00272             }
00273             buf[counter + j] = temp[counter] >> (j * 8);
00274 
00275         }
00276         counter++;
00277     }
00278 }
00279 
00280 void I2S::read(int buf[], int len)
00281 {
00282     bool len_valid = true;
00283     if (len <= 0)
00284         return;
00285     if (len >= fifo_points())
00286         len = fifo_points();
00287     int temp[8];
00288     int counter = 0;
00289     int increment = 32 / wordwidth;
00290     int fifo_levl = fifo_level();
00291     while (counter < fifo_levl && len_valid) {
00292         temp[counter] = LPC_I2S->RXFIFO;
00293         for (int j = 0; j < increment; j++) {
00294             if ((counter * increment) + j > len) {
00295                 len_valid = false;
00296                 break;
00297             }
00298             buf[counter + j] = temp[counter] >> (j * wordwidth);
00299 
00300         }
00301         counter++;
00302     }
00303 }
00304 
00305 void I2S::read(int bufr[], int bufl[], int len)
00306 {
00307     //#TODO: Write this
00308 }
00309 
00310 int I2S::max_fifo_points()
00311 {
00312     switch (wordwidth) {
00313         case 8:
00314             return (4 * 8);
00315         case 16:
00316             return (2 * 8);
00317         case 32:
00318             return 8;
00319         default:
00320             return 0;
00321     }
00322 }
00323 
00324 int I2S::fifo_points()
00325 {
00326     switch (wordwidth) {
00327         case 8:
00328             return (4 * fifo_level());
00329         case 16:
00330             return (2 * fifo_level());
00331         case 32:
00332             return fifo_level();
00333         default:
00334             return 0;
00335     }
00336 }
00337 
00338 void I2S::power(bool pwr)
00339 {
00340     if (pwr) {
00341         stopped = false;
00342     } else {
00343         stopped = true;
00344     }
00345     write_registers();
00346 }
00347 
00348 void I2S::masterslave(bool mastermode)
00349 {
00350     if (mastermode == I2S_MASTER) {
00351         master = true;
00352     } else {
00353         master = false;
00354     }
00355     write_registers();
00356 }
00357 
00358 void I2S::wordsize(int words)
00359 {
00360     wordwidth = words;
00361     write_registers();
00362 }
00363 
00364 void I2S::mclk_freq(int freq)
00365 {
00366     mclk_frequency = freq;
00367     write_registers();
00368 }
00369 
00370 void I2S::frequency(int desired_freq)
00371 {
00372     freq = desired_freq;
00373     write_registers();
00374 }
00375 
00376 int I2S::fifo_level()
00377 {
00378     int level = 0;
00379     if (_rxtx == I2S_TRANSMIT) {
00380         level = LPC_I2S->STATE;
00381         level >>= 16;
00382         level &= 0xF;
00383     } else {
00384         level = LPC_I2S->STATE;
00385         level >>= 8;
00386         level &= 0xF;
00387     }
00388     return level;
00389 }
00390 
00391 void I2S::stereomono(bool stereomode)
00392 {
00393     if (stereomode == I2S_STEREO) {
00394         stereo = true;
00395     } else {
00396         stereo = false;
00397     }
00398 }
00399 
00400 void I2S::mute()
00401 {
00402     muted = true;
00403     write_registers();
00404 }
00405 
00406 void I2S::mute(bool mute_en)
00407 {
00408     muted = mute_en;
00409     write_registers();
00410 }
00411 
00412 void I2S::stop()
00413 {
00414     stopped = true;
00415     write_registers();
00416 }
00417 
00418 void I2S::set_interrupt_fifo_level(int level)
00419 {
00420     interrupt_fifo_level = level;
00421     write_registers();
00422 }
00423 
00424 void I2S::start()
00425 {
00426     stopped = false;
00427     muted = false;
00428     write_registers();
00429 }
00430 
00431 bool I2S::setup_ok()
00432 {
00433     if ((reg_write_err + pin_setup_err) > 0)
00434         return false;
00435     else
00436         return true;
00437 }
00438 
00439 void I2S::pin_setup()
00440 {
00441     pin_setup_err = 0;
00442 
00443     if (_rxtx == I2S_TRANSMIT) {
00444         //printf("\n\rSetting up pins....\n\r");
00445         if (_sd != p11)
00446             pin_setup_err++;
00447         if (_ws != p12 && ws_d == true)
00448             pin_setup_err++;
00449         if (_clk != p13 && clk_d == true)
00450             pin_setup_err++;
00451         //printf("Hmm....%i\n\r", pin_setup_err);
00452     } else {
00453         if (_sd != p14)
00454             pin_setup_err++;
00455         if (_ws != p33 && ws_d == true)
00456             pin_setup_err++;
00457         if (_clk != p34 && clk_d == true)
00458             pin_setup_err++;
00459     }
00460 
00461     if (pin_setup_err == 0) {
00462         if (_rxtx == I2S_TRANSMIT) {
00463             int val1 = 1;
00464             if (deallocating) {
00465                 val1 = 0;
00466             }
00467             LPC_IOCON->P0_9 |= ((1<<7) | (1<<0));
00468 //            LPC_PINCON->PINSEL0 |= (val1 << 18); //set p5 as transmit serial data line
00469             if (ws_d == true)
00470                 LPC_IOCON->P0_8 |= ((1<<7) | (1<<0));
00471 //                LPC_PINCON->PINSEL0 |= (val1 << 14); //set p7 as transmit clock line
00472             if (clk_d == true)
00473                 LPC_IOCON->P0_7 |= ((1<<7) | (1<<0));
00474 //                LPC_PINCON->PINSEL0 |= (val1 << 16); //set p6 as word select line
00475 
00476         } else {
00477             int val1 = 1;
00478             int val2 = 2;
00479             if (deallocating) {
00480                 val1 = 0;
00481                 val2 = 0;
00482             }
00483 
00484 //            if (_sd == p8)
00485             LPC_IOCON->P0_6 |= (1<<0);
00486 //                LPC_PINCON->PINSEL0 |= (val1 << 12);
00487 //            else
00488 //                LPC_PINCON->PINSEL1 |= (val2 << 18);
00489 
00490             if (ws_d == true) {
00491                 LPC_IOCON->P0_5 |= (1<<0);
00492 //                if (_ws == p29)
00493 //                    LPC_PINCON->PINSEL0 |= (val1 << 10);
00494 //                else
00495 //                    LPC_PINCON->PINSEL1 |= (val2 << 16);
00496             }
00497 
00498             if (clk_d == true) {
00499                 LPC_IOCON->P0_4 |= (1<<0);
00500 //                if (_clk == p15)
00501 //                    LPC_PINCON->PINSEL0 |= (val1 << 8);
00502 //                else
00503 //                    LPC_PINCON->PINSEL1 |= (val2 << 14);
00504             }
00505         }
00506     }
00507 }
00508 
00509 void I2S::write_registers()
00510 {
00511     reg_write_err = 0;
00512     //Clock Multiplier Calculations
00513     float pre_mult = 0;
00514     int pre_num = 0;
00515     int pre_den = 0;
00516     int bitrate_div = 0;
00517     if (master == true) { // In the hope of not doing all this heavy lifting every configuration
00518         //printf("Doing some clock magic..\n\r");
00519         int bitrate = freq * 64;
00520         float target_div = I2S_PCLK_RATE / float(bitrate * 2);// Work out what divider is needed in the end, including the halving of rates the smoother does
00521         if (mclk_frequency == 0) {
00522             float rnd = fmod(target_div,1);// To make the X/Y fraction closest to 1, we set the last divider to the nearest integer to the rate divider
00523             bitrate_div = int(target_div - rnd);
00524             while (bitrate_div > I2S_MAX_BITRATE_DIV) { // But this might be out of range, so we right shift it into focus
00525                 bitrate_div >>= 1;
00526             }
00527             if (bitrate_div == 0) { // Could be zero, which would disable the the clock...
00528                 bitrate_div = 1;
00529             }
00530             pre_mult = float(bitrate_div) / target_div;    // Work out what we have left to correct
00531             pre_num = 0;
00532             pre_den = 0;
00533             fraction_estimator(pre_mult, &pre_num, &pre_den);// Get the function to work out the closest fraction, there might be some point in adding some possible multipliers of these values to add to the smoothing, the reference manual (UM10360 page 480) suggests this
00534 
00535         } else {
00536             pre_mult = float(mclk_frequency * 2) / (I2S_PCLK_RATE);
00537             pre_num = 0;
00538             pre_den = 0;
00539             fraction_estimator(pre_mult, &pre_num, &pre_den);// Get the function to work out the closest fraction, there might be some point in adding some possible multipliers of these values to add to the smoothing, the reference manual (UM10360 page 480) suggests this
00540             bitrate_div = int(
00541                               I2S_PCLK_RATE * float(pre_num) / float(pre_den)
00542                               / float(bitrate));
00543         }
00544 
00545         old_freq = freq;
00546         old_pre_num = pre_num;
00547         old_pre_den = pre_den;
00548         old_bitrate_div = bitrate_div;
00549     } else {
00550         pre_num = 0; //old_pre_num;
00551         pre_den = 0; //old_pre_den;
00552         bitrate_div = 1; //old_bitrate_div;
00553     }
00554 
00555     //Clock Multiplier, MCLK setup
00556     if (_rxtx == I2S_TRANSMIT) {
00557         int regvals = ((pre_num << 8) & 0xFF00) | (pre_den & 0xFF);
00558         LPC_I2S->TXRATE = regvals;                // Setting the X/Y fraction
00559         LPC_I2S->TXBITRATE = (bitrate_div - 1) & 0x3F;// Setting up the bitrate divider, the periferal adds one to this
00560 
00561         LPC_I2S->TXMODE = fourwire << 2;
00562 
00563         if (mclk_d == true) {
00564             LPC_I2S->TXMODE |= (1 << 3);
00565         }
00566     } else {
00567         int regvals = ((pre_num << 8) & 0xFF00) | (pre_den & 0xFF);
00568         LPC_I2S->RXRATE = regvals;                // Setting the X/Y fraction
00569         LPC_I2S->RXBITRATE = (bitrate_div - 1) & 0x3F;// Setting up the bitrate divider, the periferal adds one to this
00570 
00571         LPC_I2S->RXMODE = fourwire << 2;
00572 
00573         if (mclk_d == true) {
00574             LPC_I2S->RXMODE |= (1 << 3);
00575         }
00576     }
00577 
00578     switch (wordwidth) {
00579         case 8:
00580             wordwidth_code = 0;
00581             break;
00582         case 16:
00583             wordwidth_code = 1;
00584             break;
00585         case 32:
00586             wordwidth_code = 3;
00587             break;
00588         default:
00589             reg_write_err++;
00590             break;
00591     }
00592 
00593     int I2SDA_reg = (wordwidth_code & 0x3);
00594     I2SDA_reg |= ((!stereo << 2) & 0x4);
00595     I2SDA_reg |= ((stopped << 3) & 0x8);
00596     I2SDA_reg |= ((!master << 5) & 0x20);
00597     I2SDA_reg |= (0x1F << 6);
00598     I2SDA_reg |= ((muted << 15) & 0x8000);
00599 
00600     if (_rxtx == I2S_TRANSMIT) {
00601         LPC_I2S->DAO = I2SDA_reg;
00602     } else {
00603         LPC_I2S->DAI = I2SDA_reg;
00604     }
00605 
00606     if (_rxtx == I2S_TRANSMIT) {
00607         if (txisr) {
00608             LPC_I2S->IRQ = (LPC_I2S->IRQ & 0xFF0FFFFF)
00609                               | ((interrupt_fifo_level & 0xF) << 16);
00610             LPC_I2S->IRQ |= 0x2;
00611         } else {
00612             LPC_I2S->IRQ &= 0xFFFFFFFD;
00613         }
00614     } else {
00615         if (rxisr) {
00616             LPC_I2S->IRQ = (LPC_I2S->IRQ & 0xFFFFF0FF)
00617                               | ((interrupt_fifo_level & 0xF) << 8);
00618             LPC_I2S->IRQ |= 0x1;
00619         }
00620 
00621         else {
00622             LPC_I2S->IRQ &= 0xFFFFFFFE;
00623         }
00624     }
00625 }
00626 
00627 void I2S::_i2sisr(void)
00628 {
00629     I2STXISR.call();
00630     I2SRXISR.call();
00631 }
00632 
00633 // A function to find the nearest fraction to that put to it, with numerator and denomnator less than 256
00634 // This is used when trying to get the clocks correct
00635 
00636 void I2S::fraction_estimator(float in, int * num, int * den)
00637 {
00638     int test_num = 0;
00639     int test_den = 0;
00640     float least_error = 1;
00641     int least_err_den = 0;
00642     float genval;
00643     float generr;
00644 
00645     for (test_den = 1; test_den < I2S_MAX_DENOMINATOR; test_den++) {
00646         test_num = int(float(test_den) * in);
00647         if (test_num < I2S_MAX_NUMERATOR && test_num > 0) {
00648             genval = float(test_num) / float(test_den);
00649             generr = mod(genval - in);
00650             if (generr < least_error) {
00651                 least_error = generr;
00652                 least_err_den = test_den;
00653             }
00654             if (generr == 0) {
00655                 break;
00656             }
00657         }
00658     }
00659 
00660     test_num = int(float(least_err_den) * in);
00661     *num = test_num;
00662     *den = least_err_den;
00663 
00664 }
00665 
00666 float I2S::mod(float in)
00667 {
00668     if (in < 0)
00669         in *= -1;
00670 
00671     return in;
00672 }