Generate sine waves with 2 mbeds synchronised. Configurable amplitude and phase. Built for 50 Hz mains simulations.

Dependencies:   MODDMA mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002  *  2 Channel DAC WaveSimulator with 2 lpc1768 mbed's. 
00003  *  Command line or GUI controlled.
00004  */
00005 
00006 #include "mbed.h"
00007 #include "MODDMA.h"
00008 #include <cmath>
00009 #include "watchdog.h"
00010 #include "xIFO.h"
00011 #include <stdio.h>
00012 #include <stdlib.h>
00013 #include <ctype.h>
00014 
00015 Serial pc(USBTX, USBRX);
00016 Serial sr(p9, p10);
00017 Watchdog wdt;
00018 
00019 const double PI   = 3.141592653589793238462;
00020 const float  PI_F = 3.14159265358979f;
00021  
00022 // Make the buffer size match the number of degrees
00023 // in a circle since we are going to output a sinewave.
00024 #define BUFFER_SIZE 360
00025  
00026 // Set DAC output power mode.
00027 #define DAC_POWER_MODE  (1 << 16)
00028  
00029 DigitalOut led1(LED1);
00030 DigitalOut led2(LED2);
00031 DigitalOut led3(LED3);
00032 DigitalOut led4(LED4);
00033 DigitalIn     modesel(p22);   // mode selector
00034 DigitalInOut trigger(p21);    // sync trigger output
00035  
00036 uint32_t buffer[3][BUFFER_SIZE];
00037  
00038 AnalogOut signal(p18);      /* Do not forget output RC filter! */
00039  
00040 MODDMA dma;
00041 MODDMA_Config *conf0, *conf1, *conf2, *conf3;
00042  
00043 // Config phase (2*pi is one period)
00044 double phase_master         = PI * 1;
00045 double phase_slave             = PI * 1;
00046 // Config amplitude, x * 3.3 Volt
00047 double amplitude_master    =    0.75;
00048 double amplitude_slave    = 0.75;
00049 // Config frequency, (if using synchronisation these must be identical)
00050 double freq_master            = 50;
00051 double freq_slave                = freq_master;
00052 // copy calc buffer[2] to ch0/1
00053 volatile uint32_t toch0 = 0;
00054 volatile uint32_t toch1 = 0;
00055 // Mode typedef
00056 typedef enum {MASTER, SLAVE} modesel_t;
00057 modesel_t mode;
00058 // Ringbuffer for cmd
00059 xifo_t com;
00060 xifo_pool_t compool[128]; 
00061  
00062 /* Dma callbacks */
00063 void TC0_callback(void);
00064 void ERR0_callback(void);
00065  
00066 void TC1_callback(void);
00067 void ERR1_callback(void);
00068 
00069 // Sync function
00070 void wait_for_sync(){
00071     switch(mode){
00072         case MASTER:
00073             // Wait a few clocks to make sure slave is waiting
00074             for(uint32_t i=0; i<10; i++){ __NOP(); __NOP(); __NOP(); __NOP(); }
00075             // Trigger toggle
00076             LPC_GPIO2->FIOPIN ^= (1<<5);
00077         break;
00078         case SLAVE:
00079             // Wait for pinchange
00080             {
00081             uint32_t pinstate = trigger;
00082             while( pinstate == trigger);
00083             }
00084         break;
00085     }
00086 }
00087  
00088 // Calculate function
00089 void calculate_sines(){
00090      // Create a sinewave buffer for testing.            
00091         switch(mode){
00092         case MASTER:
00093             for (int i =   0; i <=  359; i++)
00094             {
00095                     double rads = (PI/180.0 * i);
00096                     buffer[0][i] =  amplitude_master * (512*( cos( rads + phase_master )  ))+512;                
00097             }
00098         break;
00099         case SLAVE:
00100             for (int i =   0; i <=  359; i++)
00101             { 
00102                 double rads = (PI/180.0 * i);
00103                 buffer[0][i] =  amplitude_slave * (512 * cos(rads + phase_slave)) + 511;                
00104             }
00105         break;
00106         }
00107         
00108     // Adjust the sinewave buffer for use with DAC hardware.
00109     for (int i = 0; i < 360; i++) {
00110                 if( buffer[0][i] > 1023 ) buffer[0][i] = 1023;
00111         buffer[0][i] = DAC_POWER_MODE | ((buffer[0][i] << 6) & 0xFFC0);    
00112         buffer[1][i] = buffer[0][i]; // Just create a copy of buffer0 to continue sinewave.
00113     }
00114 }
00115  
00116 void copycallback(){
00117         if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 
00118 }
00119  
00120 // Main
00121 int main() {
00122     volatile int life_counter = 0;
00123         pc.baud(115200);
00124         
00125         if(modesel == 1){
00126             pc.printf("slave\r\n");
00127             trigger.input();
00128             mode = SLAVE;
00129             led2 = 0;
00130         }else{
00131           pc.printf("master\r\n");
00132             trigger.output();
00133             led2 = 1;
00134             mode = MASTER;
00135                     // Print command set
00136         pc.printf("Command descriptions: <[master][slave]><[frequency][amplitude][phase]> <number>\r\n");
00137         pc.printf("Commands: <[m][s]><[f][a][p]> <n>\r\n");
00138         pc.printf("Min F = 1.02 Hz\r\n");
00139         pc.printf("Max F limited to synchronisation and DAC speed (200 Hz with sync, max 1000 Hz without)\r\n");
00140         }
00141     
00142     calculate_sines();
00143     
00144     // Prepare the GPDMA system for buffer0.
00145     conf0 = new MODDMA_Config;
00146     conf0
00147      ->channelNum    ( MODDMA::Channel_0 )
00148      ->srcMemAddr    ( (uint32_t) &buffer[0] )
00149      ->dstMemAddr    ( MODDMA::DAC )
00150      ->transferSize  ( 360 )
00151      ->transferType  ( MODDMA::m2p )
00152      ->dstConn       ( MODDMA::DAC )
00153      ->attach_tc     ( &TC0_callback )
00154      ->attach_err    ( &ERR0_callback )     
00155     ; // config end
00156     
00157     // Prepare the GPDMA system for buffer1.
00158     conf1 = new MODDMA_Config;
00159     conf1
00160      ->channelNum    ( MODDMA::Channel_1 )
00161      ->srcMemAddr    ( (uint32_t) &buffer[1] )
00162      ->dstMemAddr    ( MODDMA::DAC )
00163      ->transferSize  ( 360 )
00164      ->transferType  ( MODDMA::m2p )
00165      ->dstConn       ( MODDMA::DAC )
00166      ->attach_tc     ( &TC1_callback )
00167      ->attach_err    ( &ERR1_callback )     
00168     ; // config end
00169 
00170                         
00171     // Calculating the transfer frequency:
00172     // By default, the Mbed library sets the PCLK_DAC clock value
00173     // to 24MHz. One complete sinewave cycle in each buffer is 360
00174     // points long. So, for a 1Hz wave we would need to transfer 360
00175     // values per second. That would be 24000000/360 which is approx
00176     // 66,666. But that's no good! The count val is only 16bits in size
00177     // so bare this in mind. If you need to go slower you will need to
00178     // alter PCLK_DAC from CCLK/4 to CCLK/8.
00179     // For our demo we are going to have the sinewave run at 1kHz.
00180     // That's 24000000/360000 which is approx 66. Experimentation
00181     // however showed 65 to get closer to 1kHz (on my Mbed and scope 
00182     // at least).
00183         const double dacclk = 24000000;
00184         const double dacper = dacclk / 360;
00185         double ddacdiv;
00186         switch(mode){
00187         case MASTER:
00188             ddacdiv = dacper / (freq_master);    
00189         break;
00190         case SLAVE:
00191           ddacdiv = dacper / (freq_slave);
00192         break;
00193         }
00194         unsigned int dacdiv = ddacdiv;
00195     LPC_DAC->DACCNTVAL = dacdiv; // 6500 for 10Hz
00196         
00197         // Watchdogtimer to reset if sync failed
00198         wdt.kick(10.0*(1.0/(dacper/ddacdiv))); 
00199  
00200     // Prepare first configuration.
00201     if (!dma.Prepare( conf0 )) {
00202         error("Doh!");
00203     }
00204     // Wait period for master (slaves waits for sync)
00205         if(mode == MASTER){
00206             wait(0.1);
00207         }
00208         wait_for_sync();
00209         
00210     // Begin (enable DMA and counter). Note, don't enable
00211     // DBLBUF_ENA as we are using DMA double buffering.
00212     LPC_DAC->DACCTRL |= (3UL << 2);
00213     
00214         // Disable copy calc buffer flags
00215         toch0 = 0; 
00216         toch1 = 0;
00217         
00218         // Create ringbuffer for parsing
00219         xifo_init(&com, 128, (uint32_t *)&compool); 
00220         xifo_clear(&com);
00221         uint32_t do_calc=0;
00222 
00223     while (1) {
00224             // There's not a lot to do as DMA and interrupts are
00225             // now handling the buffer transfers. So we'll just
00226             // flash led1 to show the Mbed is alive and kicking.
00227             if (life_counter++ > 1000000) {
00228                     //led1 = !led1; // Show some sort of life.
00229                     life_counter = 0;
00230             }
00231             
00232             /* Do UART data processing */
00233             if(mode==MASTER){
00234                 while(pc.readable()){
00235                     xifo_write(&com, pc.getc());
00236                 }
00237                 while(sr.readable()){
00238                     pc.putc(sr.getc());
00239                 }
00240             }else{
00241                 while(sr.readable()){
00242                 xifo_write(&com, sr.getc());
00243                 }
00244             }
00245                       
00246             
00247             
00248             {    /* BLOCK with command parsing */
00249                 uint32_t do_parse=0;
00250                 // 123456.123456 accurate
00251                 char number[13] = {0,0,0,0,0,0,0,0,0,0,0,0,0};
00252                 uint32_t p=0;                
00253                 // Only parse USB master commands on master mode
00254                 if(mode==MASTER){
00255                     // MASTER mode
00256                     if( xifo_read_mr(&com,0) == '\n'){
00257                     xifo_pop_mr(&com);
00258                         // We have a line
00259                         if( xifo_read_lr(&com,0)== 's' ){                                    // Slave
00260                             xifo_pop_lr(&com);
00261                             pc.printf("Slave ");
00262                             // command for slave, forward over serial sr
00263                             while(xifo_get_used(&com))
00264                             sr.putc(xifo_pop_lr(&com));
00265                             sr.putc('\n');
00266                             xifo_init(&com, 128, (uint32_t *)&compool); 
00267                             xifo_clear(&com);
00268                         } else if( xifo_read_lr(&com,0) == 'm' ){                                // Master
00269                             xifo_pop_lr(&com);
00270                             pc.printf("Master ");
00271                             // master
00272                             // Parsing
00273                             do_parse =1;
00274                         } else if( xifo_read_lr(&com,0) == 'f' ){                                // Frequency on MASTER and SLAVE
00275                             // Parse data
00276                             xifo_pop_lr(&com); // space
00277                             // Get number
00278                             while( xifo_get_used(&com) && p < sizeof(number))
00279                                 number[p++] = (char)xifo_pop_lr(&com);
00280                             freq_master = strtod(number,0);
00281                                 int t = dacper / (freq_master);
00282                                 pc.printf("Master Frequency %f, approximate: %f\nSlave ", freq_master, dacper/t);
00283                                 sr.printf("f %f\n", freq_master);
00284                                 do_calc = 1;
00285                         } else {
00286                             pc.printf("fout ");
00287                             while(xifo_get_used(&com))
00288                                     pc.putc(xifo_pop_lr(&com));
00289                             pc.putc('\n');
00290                             xifo_init(&com, 128, (uint32_t *)&compool); 
00291                             xifo_clear(&com);
00292                         }}
00293                 }else{
00294                     // SLAVE mode
00295                     if( xifo_read_mr(&com,0) == '\n'){
00296                         xifo_pop_mr(&com);
00297                         do_parse = 1;
00298                     }
00299                 }
00300                 if(do_parse){
00301                     do_parse=0;
00302                     
00303                     // Parse data
00304                     char filter = xifo_pop_lr(&com);
00305                     xifo_pop_lr(&com) ; // space
00306                     // Get number
00307                     while( xifo_get_used(&com) && p < sizeof(number))
00308                         number[p++] = (char)xifo_pop_lr(&com);
00309                     
00310                     if(mode==MASTER){    
00311                         // frequency
00312                         if( filter == 'f' ){
00313                             freq_master = strtod(number,0);
00314                             int t = dacper / (freq_master);
00315                             pc.printf("Frequency %f, approximate: %f\n", freq_master, dacper/t);
00316                             do_calc = 1;
00317                         }else{
00318                         // amplitude
00319                         if( filter == 'a'){
00320                             amplitude_master = strtod(number,0);
00321                             pc.printf("Amplitude %f\n", amplitude_master);
00322                             do_calc = 1;
00323                         }else{
00324                         // phase
00325                         if( filter == 'p' ){
00326                             phase_master = strtod(number,0) * PI;
00327                             pc.printf("Phase %f\n", phase_master);
00328                             do_calc = 1;                            
00329                         } else{
00330                         pc.printf("fout ");
00331                         while(xifo_get_used(&com))
00332                                 pc.putc(xifo_pop_lr(&com));
00333                         pc.putc('\n');
00334                         xifo_init(&com, 128, (uint32_t *)&compool); 
00335                         xifo_clear(&com);
00336                         }}}
00337                     }else{
00338                         // frequency
00339                         if( filter == 'f' ){
00340                             freq_slave = strtod(number,0);
00341                             int t = dacper / (freq_slave);
00342                             sr.printf("Frequency %f, approximate: %f\n", freq_slave, dacper/t);
00343                             do_calc = 1;
00344                         }else{
00345                         // amplitude
00346                         if( filter == 'a'){
00347                             amplitude_slave = strtod(number,0);
00348                             sr.printf("Amplitude %f\n", amplitude_slave);
00349                             do_calc = 1;
00350                         }else{
00351                         // phase
00352                         if( filter == 'p' ){
00353                             phase_slave = strtod(number,0) * PI;
00354                             sr.printf("Phase %f\n", phase_slave);
00355                             do_calc = 1;                            
00356                         } else{
00357                         sr.printf("fout ");
00358                         while(xifo_get_used(&com))
00359                                 sr.putc(xifo_pop_lr(&com));
00360                         sr.putc('\n');
00361                         xifo_init(&com, 128, (uint32_t *)&compool); 
00362                         xifo_clear(&com);
00363                         }}}
00364                     }
00365                     }
00366             } /* BLOCK with command parsing */
00367 
00368             // recalculate
00369             if(do_calc){
00370                 do_calc = 0;
00371                 // only continue if previous update is complete
00372                 if( !toch0 && !toch1 ){
00373                     // calc frequency
00374                         switch(mode){
00375                         case MASTER:
00376                             ddacdiv = dacper / (freq_master);
00377                             //pc.printf("set dacdiv to %f\n",ddacdiv);
00378                         break;
00379                         case SLAVE:
00380                             ddacdiv = dacper / (freq_slave);
00381                         break;
00382                         }
00383                         unsigned int dacdiv = ddacdiv;
00384                         LPC_DAC->DACCNTVAL = dacdiv; // 6500 for 10Hz
00385                         wdt.kick(10.0*(1.0/(dacper/ddacdiv)));
00386                     // calculate_sines sine
00387                     switch(mode){
00388                     case MASTER:
00389                         for (int i =   0; i <=  359; i++) 
00390                         {
00391                             double rads = (PI/180.0 * i);
00392                             buffer[2][i] =  amplitude_master * (512 * cos( rads + phase_master)) + 512;                        
00393                         }
00394                     break;
00395                     case SLAVE:
00396                         for (int i =   0; i <=  359; i++)
00397                         {
00398                             double rads = (PI/180.0 * i);
00399                             buffer[2][i] =  amplitude_slave * (512 * cos( rads + phase_slave)) + 512;                
00400                         }
00401                     break;
00402                     }
00403                     // Adjust the sinewave buffer for use with DAC hardware.
00404                     for (int i = 0; i < 360; i++) {
00405                             if( buffer[2][i] > 1023 ) buffer[2][i] = 1023;
00406                             buffer[2][i] = DAC_POWER_MODE | ((buffer[2][i] << 6) & 0xFFC0);    
00407                     }
00408                 toch0 = 1; 
00409                 toch1 = 1;
00410             }
00411         }
00412     }
00413 }
00414  
00415 // Configuration callback on TC
00416 void TC0_callback(void) {
00417 
00418     // Just show sending buffer0 complete.
00419     led3 = !led3; 
00420     
00421     // Implement wait for trigger if slave mode
00422         wait_for_sync();
00423         wdt.kick();
00424             
00425     // Get configuration pointer.
00426     MODDMA_Config *config = dma.getConfig();
00427     
00428     // Finish the DMA cycle by shutting down the channel.
00429     dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
00430    
00431     // Swap to buffer1
00432     dma.Prepare( conf1 );
00433  
00434     // Clear DMA IRQ flags.
00435     if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 
00436 
00437         // Copy to channel 0?    
00438         if ( toch0 ){
00439             for (int i =   0; i <=  359; i++) buffer[0][i] =  buffer[2][i];
00440             toch0=0;
00441         }
00442 }
00443  
00444 // Configuration callback on Error
00445 void ERR0_callback(void) {
00446     error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem");
00447 }
00448  
00449 // Configuration callback on TC
00450 void TC1_callback(void) {
00451     // Just show sending buffer1 complete.
00452     //led4 = !led4; 
00453    
00454     // Implement wait for trigger if slave mode
00455         wait_for_sync();
00456         wdt.kick();
00457      
00458     // Get configuration pointer.
00459     MODDMA_Config *config = dma.getConfig();
00460 
00461         // Finish the DMA cycle by shutting down the channel.
00462         dma.Disable( (MODDMA::CHANNELS)config->channelNum() );
00463         
00464         // Swap to buffer0
00465         dma.Prepare( conf0 );
00466         
00467         // Clear DMA IRQ flags.
00468         if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq();
00469       
00470         // Copy to channel 1? 
00471         if ( toch1 ){
00472             for (int i =   0; i <=  359; i++) buffer[1][i] =  buffer[2][i];
00473             toch1=0;
00474         }
00475 }
00476  
00477 // Configuration callback on Error
00478 void ERR1_callback(void) {
00479     error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem");
00480 }
00481