5 years, 7 months ago.

Using pc.getc() and interrupts

Hello mbed community!

My code below generates waveforms and scales them, based on inputs from the keyboard. One key specifies one of four waveforms, and another key specifies the peak to peak output voltage. I must enter both keys in order for anything to happen. All of this is fine, but once those keys are entered, I cannot change the waveform or output voltage without resetting the mbed and entering two new keys.

Ideally, I would like to be able to switch between waveforms and voltages real-time. I have tried researching interrupts but cannot figure it out. Any help is greatly appreciated!

main.cpp

#include "mbed.h"
#include "mcp4725.h"
#include "math.h"


Serial pc(USBTX, USBRX);
SPI spi(p11, p12, p13); // SPI Bus (mosi, miso, sclk)
DigitalOut cs(p14); // Chip Select Pin for DigiPot


// Class instantiation (similar to Serial pc(USBTX, USBRX) in function)
MCP4725 wave(p9, p10, MCP4725::Fast400kHz, 0);


const double PI = 3.1415926535897932384626433832795; // For the Sine Waves
int DesiredVoltage; // Function Parameter
unsigned char VoltageCode; // Function Parameter


/*------------------------------Function Prototypes---------------------------------------*/
unsigned char VoltageLevel (int DesiredVoltage); // Function to seperate possible levels
void ScaleVoltage (unsigned char VoltageCode); // Function to write to digipot


int main()
{  
  // Print to terminal       
  pc.printf("Starting DAC Output.\n"); 
  pc.printf("First enter the Desired Waveform.\n"); 
  pc.printf("Then select the DigiPot Output Voltage.\n"); 

  char Waveform = pc.getc();
  int DesiredVoltage = pc.getc(); 
 
  while(1)
  {            

   /*-------------------------Stop DAC Output-----------------------------*/ 
   if(Waveform == 'O') // Letter, not zero
   {
    pc.printf("Message Recieved. Stopping output.");
     wave.write(MCP4725::Normal, (0x000), false); 
   }
        
       
   /*-------------------------1-Hz Square Wave----------------------------*/ 
   if(Waveform == 'A')
   {
     wave.write(MCP4725::Normal, (0xFFF), false);
     wait(0.5);           
     wave.write(MCP4725::Normal, (0x000), false);
     wait(0.5);  
     
     VoltageLevel(DesiredVoltage);
     ScaleVoltage(VoltageCode);  
    }
      
   
   /*------------------------10-Hz Square Wave----------------------------*/         
   if(Waveform == 'B')
   {
     wave.write(MCP4725::Normal, (0xFFF), false);
     wait(0.05);           
     wave.write(MCP4725::Normal, (0x000), false);
     wait(0.05);
     
     VoltageLevel(DesiredVoltage);
     ScaleVoltage(VoltageCode);          
   }
      
   
   /*--------------------------1-Hz Sine Wave-----------------------------*/         
   if(Waveform == 'C')
   {     
     for(int i = 0; i <= 1000; i++)
     {
      wave.write(MCP4725::Normal, (0xFFF*(0.5*sin(2*PI*i/1000)+0.5)), false);
      wait_us(860);         
     }  
     
     VoltageLevel(DesiredVoltage);
     ScaleVoltage(VoltageCode);  
   }
      
   
   /*--------------------------10-Hz Sine Wave----------------------------*/         
   if(Waveform == 'D')
   {
     for(int i = 0; i <= 1000; i++)
     {
      wave.write(MCP4725::Normal, (0xFFF*(0.5*sin(10*2*PI*i/1000)+0.5)), false);
      wait_us(860);         
     }
     
     VoltageLevel(DesiredVoltage);
     ScaleVoltage(VoltageCode);  
   }   
      
  } // End while loop 
 
} //End main 


/*------------------Function to Separate Voltage Levels--------------------*/
unsigned char VoltageLevel (int DesiredVoltage)
{   
    if(DesiredVoltage == '0')
    {
      VoltageCode = 0x00;
    }    
    
    if(DesiredVoltage == '1') // For 1Vpp output
    {
      VoltageCode = 0x26;
    }
    
    if(DesiredVoltage == '2') // For 2Vpp output
    {
      VoltageCode = 0x4D;
    }    
    
    if(DesiredVoltage == '3') // For 3Vpp output
    {
      VoltageCode = 0x74;
    } 
    
    if(DesiredVoltage == '4') // For 4Vpp output
    {
      VoltageCode = 0x9B;
    } 
    
    if(DesiredVoltage == '5') // For 5Vpp output
    {
      VoltageCode = 0xC3;
    }   
    
    return VoltageCode;      
}  


/*------------------------Function to Write to Digipot--------------------------------------*/  
void ScaleVoltage (unsigned char VoltageCode)
{    
      // Chip must be deselected to begin
      cs = 1;
    
      // Setup the spi for 8 bit data, high steady state clock,
      // second edge capture, with a 1MHz clock rate
      spi.format(8,3);
      spi.frequency(1000000);
    
      // Select the device
      cs = 0;
    
      spi.write(0x11); // Command Byte
      spi.write(VoltageCode); // Set Wiper Position
    
      // Deselect the device
      cs = 1;      
}       

You can do as follows

        pc.attach(&getKey,pc.RxIrq);

and you need a routine to be serviced....

char getKey (void){
                char gotkey; 
                pc.attach(0);
                gotkey=pc.getc();
                //Might want some sanity/correct data entry checks here...
                pc.attach(&getKey,pcRxIrq);
                return gotkey;
}
posted by Martin Simpson 08 Dec 2015

2 Answers

5 years, 7 months ago.

No need to use interrupts for this, just check for key presses in your while loop and change settings based on what they press.

  while(1)
  {            
     if (pc.readable()) {                // if they have pressed a key
        char tmp = pc.getc();
        if ((tmp >= '0') && (tmp <= '9')   // if it's a number set the voltage
          DesiredVoltage  = tmp;
        else
           Waveform = tmp;                   // otherwise set the pattern
     }
  ...

Also for some reason you have a function that returns a value which you ignore and instead rely on it also setting a global variable. That's a good way to get very confused in the future. You should either use the returned value or no return anything and only use the global, the way you have it you can get some very confusing scoping problems.

The code below is (to me at least, personal preferences play a part in this sort of thing) somewhat cleaner. Or it would be if I hadn't kept the old code in there commented out to make it clearer what had changed. I removed the global variables, switched the long lists of if statements to using switch statements and added code to cope with invalid options.

#include "mbed.h"
#include "mcp4725.h"
#include "math.h"
 
 
Serial pc(USBTX, USBRX);
SPI spi(p11, p12, p13); // SPI Bus (mosi, miso, sclk)
DigitalOut cs(p14); // Chip Select Pin for DigiPot
 
 
// Class instantiation (similar to Serial pc(USBTX, USBRX) in function)
MCP4725 wave(p9, p10, MCP4725::Fast400kHz, 0);
 
 
const double PI = 3.1415926535897932384626433832795; // For the Sine Waves

// No need to declare these as globals, You end up with multiple variables with the same name, which one you get depends on where you are in the code.
//int DesiredVoltage; // Function Parameter
//unsigned char VoltageCode; // Function Parameter
 
 
/*------------------------------Function Prototypes---------------------------------------*/
unsigned char VoltageLevel (char DesiredVoltage); // Function to seperate possible levels
void ScaleVoltage (unsigned char VoltageCode); // Function to write to digipot
 
 
int main()
{  
  // Print to terminal       
  pc.printf("Starting DAC Output.\n"); 
  pc.printf("First enter the Desired Waveform.\n"); 
  pc.printf("Then select the DigiPot Output Voltage.\n"); 
 
  char Waveform = pc.getc();
  char DesiredVoltage = pc.getc();  // changed from int to char

  // no need to set the divider every time, just set it once at the start.
  unsigned char VoltageCode = VoltageLevel(DesiredVoltage);
  ScaleVoltage(VoltageCode);  

 
  while(Waveform != 'O')
  {            
     if (pc.readable()) {                // if they have pressed a key
        char tmp = pc.getc();
        if ((tmp >= '0') && (tmp <= '9')  {  // if it's a number 
          DesiredVoltage  = tmp;             // set the voltage code value
          VoltageCode = VoltageLevel(DesiredVoltage);  // set the pot
          ScaleVoltage(VoltageCode);  
        }  else
           Waveform = tmp;                   // otherwise set the pattern
     }
 
   /*-------------------------Stop DAC Output-----------------------------*/ 
   switch (Waveform) {
     case 'O':
      pc.printf("Message Recieved. Stopping output.");
      wave.write(MCP4725::Normal, (0x000), false); 
    break;        

// example of how to do the same thing for multiple inputs...
     case 'A' :       
// this line only runs for 'A'
    case 'a':
// this runs for both 'A' and 'a'    
     wave.write(MCP4725::Normal, (0xFFF), false);
     wait(0.5);           
     wave.write(MCP4725::Normal, (0x000), false);
     wait(0.5);  
     
// done once on a value change     
//     VoltageLevel(DesiredVoltage);  
//     ScaleVoltage(VoltageCode);          

  break; // don't forget this or it will fall through to the next section below.
      
   
   /*------------------------10-Hz Square Wave----------------------------*/         
   case 'B':
     wave.write(MCP4725::Normal, (0xFFF), false);
     wait(0.05);           
     wave.write(MCP4725::Normal, (0x000), false);
     wait(0.05);

// done once on a value change     
//     VoltageLevel(DesiredVoltage);  
//     ScaleVoltage(VoltageCode);          
break;
   
   /*--------------------------1-Hz Sine Wave-----------------------------*/         
case 'C':
case 'c':  
     for(int i = 0; i <= 1000; i++)
     {
      wave.write(MCP4725::Normal, (0xFFF*(0.5*sin(2*PI*i/1000)+0.5)), false);
      wait_us(860);         
     }  
     
// done once on a value change     
//     VoltageLevel(DesiredVoltage);  
//     ScaleVoltage(VoltageCode);          
  break;      
   
   /*--------------------------10-Hz Sine Wave----------------------------*/         
case 'D':
     for(int i = 0; i <= 1000; i++)
     {
      wave.write(MCP4725::Normal, (0xFFF*(0.5*sin(10*2*PI*i/1000)+0.5)), false);
      wait_us(860);         
     }
     
// done once on a value change     
//     VoltageLevel(DesiredVoltage);  
//     ScaleVoltage(VoltageCode);          
  break;

    default:  // what to do if they picked some other letter
      pc.printf("Invalid option. Please select a waveform type");
       wave.write(MCP4725::Normal, (0x000), false); 
      Waveform = pc.getc();
      break;
    } // end switch
 
} //End main 
 
 
/*------------------Function to Separate Voltage Levels--------------------*/
unsigned char VoltageLevel (char DesiredVoltage)
{   
    switch (DesiredVoltage) {
      case '0':  // 0Vpp
      default:    // wasn't a valid level, disable output
        return 0x00;
        break;  // technically not needed due to the return above.
      case '1':  // 1Vpp
        return  0x26;
        break;
      case '2':  // 2Vpp
        return   0x4D;
        break;
      case '3':  // 3Vpp
        return  0x74;
        break;
      case '4':  // 4Vpp
        return  0x9B;
        break;
      case '5':  // 5Vpp
        return  0xC3;
        break;
    }    
}  
 
 
/*------------------------Function to Write to Digipot--------------------------------------*/  
void ScaleVoltage (unsigned char VoltageCode)
{    
      // Chip must be deselected to begin
      cs = 1;
    
      // Setup the spi for 8 bit data, high steady state clock,
      // second edge capture, with a 1MHz clock rate
      spi.format(8,3);
      spi.frequency(1000000);
    
      // Select the device
      cs = 0;
    
      spi.write(0x11); // Command Byte
      spi.write(VoltageCode); // Set Wiper Position
    
      // Deselect the device
      cs = 1;      
} 

Accepted Answer

Yes!! That works great! Thank you Andy I really appreciate your help and the comments in the code!

posted by Sean Solley 08 Dec 2015

Just to note this solution will block until the wave form has completed (upto 1 Second with 1Hz) where as interupts will allow to change on demand. Not to say Andy's work is wrong just different way to a solution it depends on your application. Kind Regards

posted by Martin Simpson 08 Dec 2015

Errrr. No. Blocking implies execution is paused for some reason e.g. calling getc() twice when there may only be one byte waiting. That can happen even in an interrupt (see your code above for an example of blocking code).

It is true that the wave will only switch pattern at the end of a cycle but that is also true of an interrupt based system unless you rewrote the entire program to generate the outputs based on a ticker. In that situation the main loop wouldn't then be generating the pattern and so could look for key presses meaning that you still don't need interrupts for the key detection.

Thinking about it a bit more the if (pc.readable()) should be while() rather than if() so that you can change pattern and amplitude at the same time if you hit the keys fast enough.

posted by Andy A 08 Dec 2015
5 years, 7 months ago.

An easy solution for your problem is to pc.getc() inside an interrupt when you microcontroler receives data. You do that by attaching one Handler, or Callback function to this interrupt. For example, I want to call HandlerRX whenever I receive data:

pc.attach(&HandlerRx, pc.RxIrq);

And that's it..

Hope I helped