Sinus Signal Generator from 18Hz to 250kHz

User avatar
HelWeb
Posts: 31
Joined: Sat Mar 23, 2019 8:39 am

Sinus Signal Generator from 18Hz to 250kHz

Postby HelWeb » Sun Apr 28, 2019 10:10 pm

From this excellent work as base
https://github.com/krzychb/dac-cosine
I build a signal generator to produce sinus signals from 18Hz to 250kHz.
From 50Hz to 100kHz the difference to the wished frequency is about 1%
And it works without CPU after initialisation.
  1. // Function generator Sin
  2. // (C) Helmut Weber
  3. //
  4. // Provided as is as example
  5. // Free for everyone
  6. //
  7. // 50 Hz - 200000Hz:     frequency error ~ 1%
  8. // from 17 Hz - 250000   possible
  9.  
  10.  
  11. // based on:
  12. // https://github.com/krzychb/dac-cosine
  13.  
  14. // clk_8m_div and frequency_step are computed to give the best result for the wished frequency
  15.  
  16.  
  17.  
  18. /* DAC Cosine Generator Example
  19.    This example code is in the Public Domain (or CC0 licensed, at your option.)
  20.    Unless required by applicable law or agreed to in writing, this
  21.    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  22.    CONDITIONS OF ANY KIND, either express or implied.
  23. */
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <stdlib.h>
  27. #include "freertos/FreeRTOS.h"
  28. #include "freertos/task.h"
  29. #include "freertos/queue.h"
  30.  
  31. #include "soc/rtc_io_reg.h"
  32. #include "soc/rtc_cntl_reg.h"
  33. #include "soc/sens_reg.h"
  34. #include "soc/rtc.h"
  35.  
  36.  
  37. #include "driver/dac.h"
  38.  
  39. /* Declare global sine waveform parameters
  40.  * so they may be then accessed and changed from debugger
  41.  * over an JTAG interface
  42.  */
  43.  
  44. // Esp32 @ 240 MHz:
  45.  
  46. // f=125.6*step/(div+1)            [div=0..7] [calc: (div+1)]
  47.  
  48. // step0:
  49. // 0=15.61Hz 1=--
  50.  
  51. // step 1:
  52. // 0 = 125.6 Hz 1 = 62.50 Hz 2 = 41.67Hz 3=31.35Hz 4=25.00Hz 5=20.92Hz 6=17.86Hz 7=15.63Hz
  53.  
  54. // step 2:125,6
  55.  
  56. // 0= 250.0 Hz 1=125Hz 2=83.68 Hz 3=62.5 Hz 4=50.1 Hz 5=41.74Hz  6=35.74 Hz  7=31.35Hz
  57.  
  58.  // step 10
  59.  // 0 = 1.248 kHz 1 = 625.4 Hz 2 = 417.4 Hz (noisy) 3 = 31.33 Hz 4 = 250.6  5 = 208.3 Hz  6=178.9 7=156.3
  60.  
  61.  // step 100:
  62.  // 0: 12.53 kHz  1 = 6.266 kHz 2=4.173kHz 3= 3.127 kHz 4=2.505 kHz 6=1.789 kHz 7=1.565KHz
  63.  
  64.  
  65. // step 500:
  66. // 0: 62.54 kHz (noisy) 1 = 31.33 kHz 2 = 20.86 kHz  5 = 10.5 kHz   6 =20.88 KhZ  7=
  67.  
  68. // step 1000:
  69. // 0 = 125 kHz, 1 = 62.11 kHz, 5 = 20.73 kHz    ==>>> 10 = 42.02 kHz 100
  70.  
  71. // step 2000
  72. // 0: 249.4 kHz(unten verzerrt)  1 = 125.8 kHz (noisy)  5 = 41.5 kHz (noisy)
  73.  
  74. int clk_8m_div = 1;      // RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency)
  75. int frequency_step = 7;  // Frequency step for CW generator
  76. int scale = 1;           // 50% of the full scale // scale 0 == fill scale = bad signal (no longer a sine wave)
  77. int offset;              // leave it default / 0 = no any offset
  78. int invert = 2;          // invert MSB to get sine waveform  // 3: invert 180°
  79.  
  80.  
  81. /*
  82.  * Enable cosine waveform generator on a DAC channel
  83.  */
  84. void dac_cosine_enable(dac_channel_t channel)
  85. {
  86.     // Enable tone generator common to both channels
  87.     SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
  88.     switch(channel) {
  89.         case DAC_CHANNEL_1:
  90.             // Enable / connect tone tone generator on / to this channel
  91.             SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M);
  92.             // Invert MSB, otherwise part of waveform will have inverted
  93.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, 2, SENS_DAC_INV1_S);
  94.             break;
  95.         case DAC_CHANNEL_2:
  96.             SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M);
  97.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2, SENS_DAC_INV2_S);
  98.             break;
  99.         default :
  100.            printf("Channel %d\n", channel);
  101.     }
  102. }
  103.  
  104.  
  105. /*
  106.  * Set frequency of internal CW generator common to both DAC channels
  107.  *
  108.  * clk_8m_div: 0b000 - 0b111
  109.  * frequency_step: range 0x0001 - 0xFFFF
  110.  *
  111.  */
  112. void dac_frequency_set(int clk_8m_div, int frequency_step)
  113. {
  114.     REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, clk_8m_div);
  115.     SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, frequency_step, SENS_SW_FSTEP_S);
  116. }
  117.  
  118.  
  119. /*
  120.  * Scale output of a DAC channel using two bit pattern:
  121.  *
  122.  * - 00: no scale
  123.  * - 01: scale to 1/2
  124.  * - 10: scale to 1/4
  125.  * - 11: scale to 1/8
  126.  *
  127.  */
  128. void dac_scale_set(dac_channel_t channel, int scale)
  129. {
  130.     switch(channel) {
  131.         case DAC_CHANNEL_1:
  132.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE1, scale, SENS_DAC_SCALE1_S);
  133.             break;
  134.         case DAC_CHANNEL_2:
  135.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE2, scale, SENS_DAC_SCALE2_S);
  136.             break;
  137.         default :
  138.            printf("Channel %d\n", channel);
  139.     }
  140. }
  141.  
  142.  
  143. /*
  144.  * Offset output of a DAC channel
  145.  *
  146.  * Range 0x00 - 0xFF
  147.  *
  148.  */
  149. void dac_offset_set(dac_channel_t channel, int offset)
  150. {
  151.     switch(channel) {
  152.         case DAC_CHANNEL_1:
  153.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC1, offset, SENS_DAC_DC1_S);
  154.             break;
  155.         case DAC_CHANNEL_2:
  156.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC2, offset, SENS_DAC_DC2_S);
  157.             break;
  158.         default :
  159.            printf("Channel %d\n", channel);
  160.     }
  161. }
  162.  
  163.  
  164. /*
  165.  * Invert output pattern of a DAC channel
  166.  *
  167.  * - 00: does not invert any bits,
  168.  * - 01: inverts all bits,
  169.  * - 10: inverts MSB,
  170.  * - 11: inverts all bits except for MSB
  171.  *
  172.  */
  173. void dac_invert_set(dac_channel_t channel, int invert)
  174. {
  175.     switch(channel) {
  176.         case DAC_CHANNEL_1:
  177.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, invert, SENS_DAC_INV1_S);
  178.             break;
  179.         case DAC_CHANNEL_2:
  180.             SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, invert, SENS_DAC_INV2_S);
  181.             break;
  182.         default :
  183.            printf("Channel %d\n", channel);
  184.     }
  185. }
  186.  
  187. /*
  188.  * Main task that let you test CW parameters in action
  189.  *
  190. */
  191. void dactask(void* arg)
  192. {
  193. double f, f_target, delta, delta_min=9999999.0;
  194. int step_target=0, divi_target=0;
  195.  
  196.   //From experiments:  f=125.6*step/(div+1)
  197.  
  198.     // this frequency is wanted:
  199.     f_target=127000.0;                             // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  200.      
  201.  
  202.     // check all combinations of step iand divi to get the best guess:
  203.     for (int step=1;step<2000; step++) {
  204.       for (int divi=0; divi<8; divi++) {
  205.         f=125.6*(double)step/(double)(divi+1);
  206.         delta= abs((f_target-f));
  207.         if (delta  < delta_min) {delta_min=delta; step_target=step; divi_target=divi; }
  208.       }
  209.     }
  210.    
  211.     clk_8m_div=divi_target;
  212.     frequency_step=step_target;
  213.    
  214.     printf("divi=%d step=%d\n",divi_target, step_target);
  215.     while(1){
  216.  
  217.         // frequency setting is common to both channels
  218.         dac_frequency_set(clk_8m_div, frequency_step);
  219.  
  220.         /* Tune parameters of channel 2 only
  221.          * to see and compare changes against channel 1
  222.          */
  223.         dac_scale_set(DAC_CHANNEL_2, scale);
  224.         dac_offset_set(DAC_CHANNEL_2, offset);
  225.         dac_invert_set(DAC_CHANNEL_2, invert);
  226.  
  227.         float frequency = RTC_FAST_CLK_FREQ_APPROX / (1 + clk_8m_div) * (float) frequency_step / 65536;
  228.         printf("THEORIE:   clk_8m_div: %d, frequency step: %d, frequency: %.0f Hz\n", clk_8m_div, frequency_step, frequency);
  229.         printf("PRACTICAL: frequency: %.0f Hz\n", 125.6*(float)frequency_step /(1 + (float)clk_8m_div) );
  230.        
  231.         printf("DAC2 scale: %d, offset %d, invert: %d\n", scale, offset, invert);
  232.         vTaskDelay(2000/portTICK_PERIOD_MS);
  233.     }
  234. }
  235.  
  236.  
  237. /*
  238.  * Generate a sine waveform on both DAC channels:
  239.  *
  240.  * DAC_CHANNEL_1 - GPIO25
  241.  * DAC_CHANNEL_2 - GPIO26
  242.  *
  243.  * Connect scope to both GPIO25 and GPIO26
  244.  * to observe the waveform changes
  245.  * in response to the parameter change
  246. */
  247. void app_main()
  248. {
  249.     dac_cosine_enable(DAC_CHANNEL_1);
  250.     dac_cosine_enable(DAC_CHANNEL_2);
  251.  
  252.     dac_output_enable(DAC_CHANNEL_1);
  253.     dac_output_enable(DAC_CHANNEL_2);
  254.  
  255.     xTaskCreate(dactask, "dactask", 1024*3, NULL, 10, NULL);
  256. }
  257.  
Fast is not fast enough :D

User avatar
TinkerBearNZ
Posts: 4
Joined: Mon Jun 17, 2019 11:20 pm

Re: Sinus Signal Generator from 18Hz to 250kHz

Postby TinkerBearNZ » Thu Jul 04, 2019 6:08 am

I'm looking at the esp32 Technical Reference Manual, section 28.5.4, and it says:
A phase-shift of 0 / 90 / 180 / 270 degrees can be added by setting register SENS_SAR_DAC_INVn[1:0].
Do you think this is just a documentation error? My experimentation with the invert values has discovered useful output with values of 2 (what's used in the demo code) and 3 (inverted), but 0 and 1 generate... odd waveforms. They look like a sign error of some sort.
SDS00001.png
SDS00001.png (13.66 KiB) Viewed 568 times

User avatar
TinkerBearNZ
Posts: 4
Joined: Mon Jun 17, 2019 11:20 pm

Re: Sinus Signal Generator from 18Hz to 250kHz

Postby TinkerBearNZ » Tue Jul 09, 2019 10:52 pm

Some interesting trade-offs using this as a waveform generator.

If you use an RTC clock divider of 1, you get lovely smooth waveforms, but you can only generate multiples of ~131.6Hz (at least on my board). Waveforms up to the 500kHz range look pretty decent.

If you use an RTC clock divider of 8, you can generate multiples of ~16.6Hz, but with 8x fewer samples, so you start seeing stepping at 100kHz.

I'm wondering if they had a use case for this, and what it was?

Who is online

Users browsing this forum: No registered users and 1 guest