Generate square wave for analog input/output on software

3.1k views Asked by At

I have a device, and it has digital i/o, analog i/o. I send to device below commands for communication.The device has gpio module. My device documantation is here

Write to digital input : gpio set/clear x
Read from digital output : gpio read x
Read from digital output : adc read x
(x : pin number)

How can I create sine/square wave and calculate amplitude? To create square wave :

  • open device
  • sleep
  • write to device low mode(t0)
  • sleep
  • write to device high mode
  • sleep
  • write to device low mode(t1)

period = (t1 - t0)

Is this a square wave?

1

There are 1 answers

3
Spektre On

it seems your example is indeed square wave

if write to device low mode(t0) sets the output pin to low and write to device low mode(t1) to high or reverse then period is the sum of the sleeps + some time for setting up GPIO state. Don't know why you have times inside the GPIO set lines and not in sleeps ... (may be something platform dependent?)

To go to sin wave

use DAC or PWM + RC filter with some precomputed amplitude table in which the index is increasing periodically.

BYTE sintab[32]={ 128,...,255,...,128,...,0,....,127  };

encoded: 128 is zero, 255 is +1 and 0 is -1; now just add some index:

int ix=0'

and once in a while (on some timer perhaps) increment it and set the output to new value:

ix=(ix+1)&31;

that and 31 just cause tho cycle the index from start again if end reached (sintab must be of size power of 2). The period is timer frequency/sintab size

[notes]

You can modify this to your purpose for example make sintab[][] a 2D array where first index means amplitude and second is the ix as now. On older platforms (MCU's) you can encode the PWM sequence directly to sintab end so on ...

You can pre-compute the sintab values like this:

sintab[ix]=128.0+127.0*sin(float(2.0*M_PI*ix)/32.0);

or if your platform supports fast enough sin you can use above line directly without the actual array ...

[edit1]

for sinwave you can use just 0/1 states. If you need analog output and:

  1. You have DAC (digital to analog converter)

    then send the actual amplitude to it like dac write sintab[ix]; that will create the analog voltage on the output pin for you.

  2. You do not have any spare DAC's use PWM Pulse Width Modulation instead

    it is an old school trick to avoid the need of DAC and still have analog output from digital pin. It works like this:

    The output value is the cumulative energy/voltage per time chunk so you generate square-wave signal

    • ratio 1:1 means that half the period is H and the rest L
    • ratio 2:1 means that 2/3 of the period is H and the rest L

    The more time the output is H the bigger output value. This is still digital output but if you connect on it any nonlinear device like capacitor or coil then the energy inertia will cause to drop the H voltage to some level dependent on the square-wave ratio. Most common is the RC filter (R is serial and C is parallel to ground). If you want to drive some coil (motor) then you do not need the filter. This kind of use usually generate high pitch sound (the PWM frequency) often heard near machinery ...

    img

    The PWM frequency has to be high enough (many times higher then the sinwave frequency)

Some code for PWM with amplitude and frequency setting:

const int timer_T=1;        // used timer interval [ms]
const int PWM_max=10;       // PWM max amplitude+1 
int PWM_s=0;                 // PWM actual step
int PWM_t=0;                 // PWM actual time

int PWM_a=3;                 // PWM amplitude <0,PWM_ratio_max)
int PWM_T=200;               // PWM period [ms]

void OnTimer()
 {
 int PWM_T0=PWM_T/PWM_max; // PWM step period must be >=1 !!!
 PWM_t+=timer_T;
 if (PWM_t>=PWM_T0)
  {
  if (PWM_s<=pwm_a) gpio set x; else gpio clear x;
  PWM_s++; if (PWM_s>=PWM_max) PWM_s=0;
  PWM_t-=PWM_T0;
  }
 }