*/\r
 #define STM32_DAC_DUAL_MODE                 FALSE\r
 #define STM32_DAC_USE_DAC1_CH1              TRUE\r
-#define STM32_DAC_USE_DAC1_CH2              FALSE\r
+#define STM32_DAC_USE_DAC1_CH2              TRUE\r
 #define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\r
 #define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\r
 #define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\r
 
             m_serial.write("S");
     }
 
+    void device::siggen_upload(dacsample_t *buffer, unsigned int size) {
+        if (connected()) {
+            uint8_t request[3] = {
+                'D',
+                static_cast<uint8_t>(size),
+                static_cast<uint8_t>(size >> 8)
+            };
+            m_serial.write(request, 3);
+
+            m_serial.write((uint8_t *)buffer, size * sizeof(dacsample_t));
+        }
+    }
+
+    void device::siggen_start() {
+        if (connected())
+            m_serial.write("W");
+    }
+
+    void device::siggen_stop() {
+        if (connected())
+            m_serial.write("w");
+    }
+
     void device::upload_filter(unsigned char *buffer, size_t size) {
         if (connected()) {
             uint8_t request[3] = {
 
     };
 
     using adcsample_t = uint16_t;
+    using dacsample_t = uint16_t;
 
     class device
     {
 
         //std::vector<adcsample_t> sample(unsigned long int count = 1);
 
+        void continuous_set_buffer_size(unsigned int size);
         void continuous_start();
         void continuous_start_measure();
         uint32_t continuous_start_get_measurement();
         std::vector<adcsample_t> continuous_read();
         void continuous_stop();
 
+        void siggen_upload(dacsample_t *buffer, unsigned int size);
+        void siggen_start();
+        void siggen_stop();
+
+        // buffer is ELF binary
         void upload_filter(unsigned char *buffer, size_t size);
         void unload_filter();
 
 
 #include <wx/sizer.h>
 #include <wx/splitter.h>
 #include <wx/statusbr.h>
+#include <wx/textdlg.h>
+
+#include <vector>
 
 enum Id {
     MeasureTimer = 1,
     MRunMeasure,
     MRunUpload,
     MRunUnload,
+    MRunGenUpload,
+    MRunGenStart,
     MCodeCompile,
     MCodeDisassemble
 };
     Bind(wxEVT_MENU, &MainFrame::onRunUnload, this, Id::MRunUnload, wxID_ANY,
          menuRun->Append(MRunUnload, "U&nload code"));
 
+    menuRun->AppendSeparator();
+    Bind(wxEVT_MENU, &MainFrame::onRunGenUpload, this, Id::MRunGenUpload, wxID_ANY,
+         menuRun->Append(MRunGenUpload, "&Load signal generator..."));
+    Bind(wxEVT_MENU, &MainFrame::onRunGenStart, this, Id::MRunGenStart, wxID_ANY,
+         menuRun->AppendCheckItem(MRunGenStart, "Start &generator"));
+
     Bind(wxEVT_MENU, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY,
          menuCode->Append(MCodeCompile, "&Compile code"));
     Bind(wxEVT_MENU, &MainFrame::onCodeDisassemble, this, Id::MCodeDisassemble, wxID_ANY,
     }
 }
 
+void MainFrame::onRunGenUpload([[maybe_unused]] wxCommandEvent&)
+{
+    if (m_device != nullptr && m_device->connected()) {
+        wxTextEntryDialog dialog (this, "Enter generator values", "Hey");
+        if (dialog.ShowModal() == wxID_OK) {
+            if (wxString values = dialog.GetValue(); !values.IsEmpty()) {
+                std::vector<stmdsp::dacsample_t> samples;
+                while (!values.IsEmpty()) {
+                    if (auto number_end = values.find_first_not_of("0123456789");
+                        number_end != wxString::npos && number_end > 0)
+                    {
+                        auto number = values.Left(number_end);
+                        if (unsigned long n; number.ToULong(&n))
+                            samples.push_back(n & 4095);
+
+                        if (auto next = values.find_first_of("0123456789", number_end + 1);
+                            next != wxString::npos)
+                        {
+                            values = values.Mid(next);
+                        } else {
+                            break;
+                        }
+                    } else {
+                        break;
+                    }
+                }
+
+                m_device->siggen_upload(&samples[0], samples.size());
+            }
+        }
+    } else {
+        wxMessageBox("No device connected!", "Run", wxICON_WARNING);
+        m_status_bar->SetStatusText("Please connect.");
+    }
+}
+
+void MainFrame::onRunGenStart(wxCommandEvent& ce)
+{
+    auto menuItem = dynamic_cast<wxMenuItem *>(ce.GetEventUserData());
+    if (m_device != nullptr && m_device->connected()) {
+        if (menuItem->IsChecked()) {
+            m_device->siggen_start();
+            menuItem->SetItemLabel("Stop &generator");
+        } else {
+            m_device->siggen_stop();
+            menuItem->SetItemLabel("Start &generator");
+        }
+    } else {
+        wxMessageBox("No device connected!", "Run", wxICON_WARNING);
+        m_status_bar->SetStatusText("Please connect.");
+    }
+}
+
 void MainFrame::onRunUpload([[maybe_unused]] wxCommandEvent&)
 {
     if (auto file = compileEditorCode(); !file.IsEmpty()) {
 
     void onRunStart(wxCommandEvent&);
     void onRunUpload(wxCommandEvent&);
     void onRunUnload(wxCommandEvent&);
+    void onRunGenUpload(wxCommandEvent&);
+    void onRunGenStart(wxCommandEvent&);
 
     void onRunCompile(wxCommandEvent&);
     void onCodeDisassemble(wxCommandEvent&);
 
 #include "dac.hpp"
 
 constexpr static const auto dacd = &DACD1;
+constexpr static const auto dacd2 = &DACD2;
 constexpr static const auto gptd = &GPTD6;
 
 constexpr static const DACConfig dac_config = {
   .dier = 0
 };
 
+static unsigned int dacs_running = 0;
+
 namespace dac
 {
     void init()
     {
         palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
-        //palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+        palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
     
         dacStart(dacd, &dac_config);
+        dacStart(dacd2, &dac_config);
         gptStart(gptd, &gpt_config);
     }
-    
-    void write_start(dacsample_t *buffer, size_t count)
+
+    void write_start(unsigned int channel, dacsample_t *buffer, size_t count)
     {
-        dacStartConversion(dacd, &dac_group_config, buffer, count);
-        gptStartContinuous(gptd, 25);
+        if (channel < 2) {
+            dacStartConversion(channel == 0 ? dacd : dacd2, &dac_group_config, buffer, count);
+
+            if (dacs_running == 0)
+                gptStartContinuous(gptd, 25);
+            dacs_running |= 1 << channel;
+        }
     }
     
-    void write_stop()
+    void write_stop(unsigned int channel)
     {
-        gptStopTimer(gptd);
-        dacStopConversion(dacd);
+        if (channel < 2) {
+            dacStopConversion(channel == 0 ? dacd : dacd2);
+
+            dacs_running &= ~(1 << channel);
+            if (dacs_running == 0)
+                gptStopTimer(gptd);
+        }
     }
 }
 
 
 namespace dac
 {
     void init();
-    void write_start(dacsample_t *buffer, size_t count);
-    void write_stop();
+    void write_start(unsigned int channel, dacsample_t *buffer, size_t count);
+    void write_stop(unsigned int channel);
 }
 
 #endif // STMDSP_DAC_HPP_
 
 CC_ALIGN(CACHE_LINE_SIZE)
 #endif
 static std::array<dacsample_t, CACHE_SIZE_ALIGN(dacsample_t, MAX_SAMPLE_BUFFER_SIZE)> dac_samples;
+#if CACHE_LINE_SIZE > 0
+CC_ALIGN(CACHE_LINE_SIZE)
+#endif
+static std::array<dacsample_t, CACHE_SIZE_ALIGN(dacsample_t, MAX_SAMPLE_BUFFER_SIZE)> dac2_samples;
 
 static uint8_t elf_file_store[MAX_ELF_FILE_SIZE];
 static elf::entry_t elf_entry = nullptr;
 }
 
 static unsigned int dac_sample_count = MAX_SAMPLE_BUFFER_SIZE;
+static unsigned int dac2_sample_count = MAX_SAMPLE_BUFFER_SIZE;
 static unsigned int adc_sample_count = MAX_SAMPLE_BUFFER_SIZE;
 static bool adc_preloaded = false;
 static bool dac_preloaded = false;
                     usbserial::read(&adc_samples[0], adc_sample_count * sizeof(adcsample_t));
                     break;
 
+                case 'b':
+                    break;
                 case 'B':
                     if (run_status == RunStatus::Idle) {
                         if (usbserial::read(&cmd[1], 2) == 2) {
                     usbserial::write(dac_samples.data(), dac_sample_count * sizeof(dacsample_t));
                     break;
                 case 'D':
-                    usbserial::read(&dac_samples[0], dac_sample_count * sizeof(dacsample_t));
+                    if (usbserial::read(&cmd[1], 2) == 2) {
+                        unsigned int count = cmd[1] | (cmd[2] << 8);
+                        if (count <= MAX_SAMPLE_BUFFER_SIZE / 2) {
+                            dac2_sample_count = count;
+                            usbserial::read(&dac2_samples[0], dac2_sample_count * sizeof(dacsample_t));
+                        } else {
+                            error_queue_add(Error::BadParam);
+                        }
+                    } else {
+                        error_queue_add(Error::BadParamSize);
+                    }
                     break;
 
                 // 'E' - Reads in and loads the compiled conversion code binary from USB.
                         if (!adc_preloaded)
                             adc::read_start(signal_operate_measure, &adc_samples[0], adc_sample_count);
                         if (!dac_preloaded)
-                            dac::write_start(&dac_samples[0], dac_sample_count);
+                            dac::write_start(0, &dac_samples[0], dac_sample_count);
                     } else {
                         error_queue_add(Error::NotIdle);
                     }
                         if (!adc_preloaded)
                             adc::read_start(signal_operate, &adc_samples[0], adc_sample_count);
                         if (!dac_preloaded)
-                            dac::write_start(&dac_samples[0], dac_sample_count);
+                            dac::write_start(0, &dac_samples[0], dac_sample_count);
                     } else {
                         error_queue_add(Error::NotIdle);
                     }
                 case 'S':
                     if (run_status == RunStatus::Running) {
                         if (!dac_preloaded)
-                            dac::write_stop();
+                            dac::write_stop(0);
                         if (!adc_preloaded)
                             adc::read_stop();
                         run_status = RunStatus::Idle;
                     }
                     break;
 
+                case 'W':
+                    dac::write_start(1, &dac2_samples[0], dac2_sample_count);
+                    break;
+                case 'w':
+                    dac::write_stop(1);
+                    break;
+
                 default:
                     break;
                 }
 {
     elf_entry = nullptr;
     if (!dac_preloaded)
-        dac::write_stop();
+        dac::write_stop(0);
     if (!adc_preloaded)
         adc::read_stop();
     error_queue_add(Error::ConversionAborted);