【二代示波器教程】第12章 示波器設計—DAC信號發生器的實現

来源:https://www.cnblogs.com/armfly/archive/2018/07/09/9284736.html
-Advertisement-
Play Games

第12章 示波器設計—DAC信號發生器的實現 本章節為大家講解二代示波器中信號發生器的實現。這個功能還是比較實用的,方便為二代示波器提供測試信號。實現了正弦波,方波和三角波的頻率,幅度以及占空比設置。 12.1 DAC的輸出阻抗和使能緩衝問題 12.2 DAC驅動實現 12.3 信號發生器配置界面設 ...


 

第12章      示波器設計—DAC信號發生器的實現

本章節為大家講解二代示波器中信號發生器的實現。這個功能還是比較實用的,方便為二代示波器提供測試信號。實現了正弦波,方波和三角波的頻率,幅度以及占空比設置。

12.1   DAC的輸出阻抗和使能緩衝問題

12.2   DAC驅動實現

12.3   信號發生器配置界面設計

12.4   信號發生器波形顯示效果

12.5   總結

 

 

12.1  DAC的輸出阻抗和使能緩衝問題

我們這裡把F429的輸出阻抗和使能緩衝問題放在最前面說。

使能了多緩衝後發現有失真問題,即滿幅輸出的時候有削頂和削底,而禁止了輸出緩衝會導致輸出阻抗僅有10KΩ左右,外接負載很容易造成分壓(可以根據實際情況,外接運放輸出)。

F429的手冊中對於DAC的幾個關鍵特性說明如下:

1、開啟緩衝的時候,外接的負載阻抗最小得是5KΩ。

2、禁止緩衝的時候,DAC輸出阻抗最大可達15KΩ,比如要實現1%精度的輸出,外接負載阻抗至少得是1.5MΩ。

3、開啟緩衝的時候,最小輸出電壓0.2V,最大Vdda - 0.2V,這個應該是造成削頂問題的根本原因。

4、禁止緩衝的時候,最小輸出電壓的典型值是0.5mV,最大輸出是Vref - 1LSB。基本正好滿幅輸出,所以效果比較好。

 

F429數據手冊中幾個關鍵參數的截圖:

 

緩衝和外接負載時的框圖:

 

禁止緩衝時,滿幅輸出效果比較漂亮:

 

使能緩衝時,滿幅輸出效果,出現削頂問題:

 

有了上面的感性認識後,下麵為大家講解DAC的驅動實現和相應的GUI界面實現。

 

12.2  DAC驅動實現

F429帶有兩個DAC,分別是DAC1和DAC2,我們這裡使用了DAC1,驅動中還需要用到TIM6和DMA,方便我們配置不同的的頻率,占空比和幅值。

 

12.2.1  第1步:引腳配置和DAC配置

配置代碼如下,使用的PA4引腳做輸出:

/*

*********************************************************************************************************

*    函 數 名: bsp_InitDAC1

*    功能說明: 配置PA4/DAC1

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

void bsp_InitDAC1(void)

{   

     /* 配置GPIO */

     {

         GPIO_InitTypeDef GPIO_InitStructure;

        

         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

        

         /* 配置DAC引腳為模擬模式  PA4 / DAC_OUT1 */

         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

         GPIO_Init(GPIOA, &GPIO_InitStructure);

     }   

 

     /* DAC通道1配置 */

     {

         DAC_InitTypeDef DAC_InitStructure;

        

         /* 使能DAC時鐘 */

         RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);    

 

         DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;  /* 選擇軟體觸發, 軟體修改DAC數據寄存器 */

         DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;

         DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;

         //DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;

         DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;

         DAC_Init(DAC_Channel_1, &DAC_InitStructure);

         DAC_Cmd(DAC_Channel_1, ENABLE);

     }

}

特別註意。程式中關閉了DAC輸出緩衝,即DAC參數成員DAC_InitStructure.DAC_OutputBuffer。關於DAC的緩衝問題,看前面12.1小節說明即可。

 

12.2.2 第2步:DAC的定時器觸發和DMA配置

DAC的定時器觸發和DMA配置如下:

/*

*********************************************************************************************************

*    函 數 名: dac1_InitForDMA

*    功能說明: 配置PA4 為DAC_OUT1, 啟用DMA2

*    形    參: _BufAddr : DMA數據緩衝區地址

*               _Count   : 緩衝區樣本個數

*             _DacFreq  : DAC樣本更新頻率

*    返 回 值: 無

*********************************************************************************************************

*/

void dac1_InitForDMA(uint32_t _BufAddr, uint32_t _Count, uint32_t _DacFreq)

{   

     uint16_t usPeriod;

     uint16_t usPrescaler;

     uint32_t uiTIMxCLK;

     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    

     DMA_Cmd(DMA1_Stream5, DISABLE);

     DAC_DMACmd(DAC_Channel_1, DISABLE);

     TIM_Cmd(TIM6, DISABLE);

    

 

     /* TIM6配置 */

     {

         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

 

         uiTIMxCLK = SystemCoreClock / 2;

        

         if (_DacFreq < 100)

         {

              usPrescaler = 10000 - 1;                         /* 分頻比 = 10000 */

              usPeriod =  (uiTIMxCLK / 10000) / _DacFreq  - 1; /* 自動重裝的值 */

         }

         else if (_DacFreq < 3000)

         {

              usPrescaler = 100 - 1;                         /* 分頻比 = 100 */

              usPeriod =  (uiTIMxCLK / 100) / _DacFreq  - 1; /* 自動重裝的值 */

         }

         else /* 大於4K的頻率,無需分頻 */

         {

              usPrescaler = 0;                     /* 分頻比 = 1 */

              usPeriod = uiTIMxCLK / _DacFreq - 1; /* 自動重裝的值 */

         }

 

         TIM_TimeBaseStructure.TIM_Period = usPeriod;

         TIM_TimeBaseStructure.TIM_Prescaler = usPrescaler;

         TIM_TimeBaseStructure.TIM_ClockDivision = 0;

         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

         TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0000;        /* TIM1 和 TIM8 必須設置 */

 

         TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

 

          /* 選擇TIM6做DAC的觸發時鐘 */

         TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);

     }

 

     /* DAC通道1配置 */

     {

         DAC_InitTypeDef DAC_InitStructure;

        

         /* 使能DAC時鐘 */

         RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);    

 

         DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;

         DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;

         DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;

         //DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;

         DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;

         DAC_Init(DAC_Channel_1, &DAC_InitStructure);

         DAC_Cmd(DAC_Channel_1, ENABLE);

     }

 

     /* DMA1_Stream5配置 */

     {

         DMA_InitTypeDef DMA_InitStructure;

 

         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

 

         /* 配置DMA1 Stream 5 channel 7用於DAC1 */

         DMA_InitStructure.DMA_Channel = DMA_Channel_7;

         DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1;

         DMA_InitStructure.DMA_Memory0BaseAddr = _BufAddr; 

         DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;     

         DMA_InitStructure.DMA_BufferSize = _Count;

         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

         DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;

         DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

         DMA_InitStructure.DMA_Priority = DMA_Priority_High;

         DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

         DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

         DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

         DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

         DMA_Init(DMA1_Stream5, &DMA_InitStructure);

         DMA_Cmd(DMA1_Stream5, ENABLE);

 

         /* 使能DAC通道1的DMA */

         DAC_DMACmd(DAC_Channel_1, ENABLE);

     }

 

     /* 使能定時器 */

     TIM_Cmd(TIM6, ENABLE);

}

 

通過這個函數可以方便的設置DAC的輸出波形頻率。計算方法是:

配置的定時器觸發頻率 / DMA的緩衝個數 = 輸出波形頻率

其中DMA緩衝數據的個數就是輸出波形一個周期的採樣點數。程式中統一將其配置為128個點代表一個周期的波形,大家實際應用中配置的點數不要太少,否則波形不夠漂亮。比如我們要出10KHz的波形,這個函數的配置就是:dac1_InitForDMA((uint32_t)&g_Wave1, 128, 10000 * 128); 數組g_Wave1裡面是128個波形採樣點。

關於這個驅動代碼,要註意TIM6的配置。F429的定時器從TIM1到TIM14的主頻如下:

/*

********************************************************************************

system_stm32f4xx.c 文件中 void SetSysClock(void) 函數對時鐘的配置如下:

 

HCLK = SYSCLK / 1     (AHB1Periph)

PCLK2 = HCLK / 2      (APB2Periph)

PCLK1 = HCLK / 4      (APB1Periph)

 

因為APB1 prescaler != 1, 所以 APB1上的TIMxCLK = PCLK1 x 2 = SystemCoreClock / 2;

因為APB2 prescaler != 1, 所以 APB2上的TIMxCLK = PCLK2 x 2 = SystemCoreClock;

 

APB1 定時器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14

APB2 定時器有 TIM1, TIM8 ,TIM9, TIM10, TIM11

 

TIM 更新周期是 = TIMCLK / (TIM_Period + 1)/(TIM_Prescaler + 1)

********************************************************************************

*/

由此可知,TIM6的主頻是SystemCoreClock / 2。當主頻是168MHz時,TIM6的時鐘就是84MHz,TIM6更新周期 = TIM6CLK / (TIM_Period + 1)/(TIM_Prescaler + 1),其中

TIM_Period就是定時器結構體成員TIM_TimeBaseStructure.TIM_Period。

TIM_Prescaler就是定時器結構體成員TIM_TimeBaseStructure.TIM_Prescaler。

另外還有非常重要的一點,TIM6是16位定時器,這兩個參範圍是0-65535,切不要超過65535。正是因為這個原因,程式中對不同的輸出頻率做了範圍區分。

 

12.2.3 第3步:正弦波輸出配置

正弦波的輸出配置如下:

/*

*********************************************************************************************************

*    函 數 名: dac1_SetSinWave

*    功能說明: DAC1輸出正弦波

*    形    參: _vpp : 幅度 0-4095;

*               _freq : 頻率

*    返 回 值: 無

*********************************************************************************************************

*/

void dac1_SetSinWave(uint16_t _vpp, uint32_t _freq)

{   

     uint32_t i;

     uint32_t dac;

    

     TIM_Cmd(TIM6, DISABLE);

        

     /* 調整正弦波幅度 */       

     for (i = 0; i < 128; i++)

     {

         dac = (g_SineWave128[i] * _vpp) / 4095;

         if (dac > 4095)

         {

              dac = 4095;  

         }

         g_Wave1[i] = dac;

     }

    

     dac1_InitForDMA((uint32_t)&g_Wave1, 128, _freq * 128);

}

正弦波輸出128個採樣點代表一個周期,同時程式裡面增加了一個幅值設置功能,範圍0到4095。實際DAC輸出的波形頻率由前面第2步函數dac1_InitForDMA實現。比如我們要實現頻率10KHz,幅值4095正弦波,那麼配置就是:dac1_SetSinWave(4095, 10000)。

 

12.2.4 第4步:方波輸出配置

方波的輸出配置如下:

/*

*********************************************************************************************************

*    函 數 名: dac1_SetRectWave

*    功能說明: DAC1輸出方波

*    形    參: _low  : 低電平時DAC,

*               _high : 高電平時DAC

*               _freq : 頻率 Hz

*               _duty : 占空比 2% - 98%, 調節步數 1%

*    返 回 值: 無

*********************************************************************************************************

*/

void dac1_SetRectWave(uint16_t _low, uint16_t _high, uint32_t _freq, uint16_t _duty)

{   

     uint16_t i;

     TIM_Cmd(TIM6, DISABLE);

    

     for (i = 0; i < (_duty * 128) / 100; i++)

     {

         g_Wave1[i] = _high;

     }

     for (; i < 128; i++)

     {

         g_Wave1[i] = _low;

     }

    

     dac1_InitForDMA((uint32_t)&g_Wave1, 128, _freq * 128);

}

方波也是輸出128個採樣點代表一個周期,同時支持幅值和占空比的配置,其中占空比可以配置2%到98%,直接填數值2到98就可以了。實際DAC輸出的波形頻率由前面第2步函數dac1_InitForDMA實現。比如我們要實現頻率10KHz,幅值4095,占空比50%的方波,那麼配置就是:dac1_SetRectWave (0, 4095, 10000, 50)。

 

12.2.5 第5步:三角波輸出配置

三角波的輸出配置如下:

/*

*********************************************************************************************************

*    函 數 名: dac1_SetTriWave

*    功能說明: DAC1輸出三角波

*    形    參: _low : 低電平時DAC,

*               _high : 高電平時DAC

*               _freq : 頻率 Hz

*               _duty : 占空比

*    返 回 值: 無

*********************************************************************************************************

*/

void dac1_SetTriWave(uint16_t _low, uint16_t _high, uint32_t _freq, uint16_t _duty)

{   

     uint32_t i;

     uint16_t dac;

     uint16_t m;

    

     TIM_Cmd(TIM6, DISABLE);

         

     /* 構造三角波數組,128個樣本,從 _low 到 _high */      

     m = (_duty * 128) / 100;

    

     if (m == 0)

     {

         m = 1;

     }

    

     if (m > 127)

     {

         m = 127;

     }

     for (i = 0; i < m; i++)

     {

         dac = _low + ((_high - _low) * i) / m;

         g_Wave1[i] = dac;

     }

     for (; i < 128; i++)

     {

         dac = _high - ((_high - _low) * (i - m)) / (128 - m);

         g_Wave1[i] = dac;

     }   

    

     dac1_InitForDMA((uint32_t)&g_Wave1, 128, _freq * 128);

}

三角波也是輸出128個採樣點代表一個周期,同時支持幅值和占空比的配置,其中占空比可以配置0%到100%,不過程式中對0%和100%做了一個特殊處理。實際DAC輸出的波形頻率由前面第2步函數dac1_InitForDMA實現。比如我們要實現頻率10KHz,幅值4095,占空比50%的三角波,那麼配置就是:dac1_SetTriWave (0, 4095, 10000, 50)。

 

12.3 信號發生器配置界面設計

信號發生器的界面設計如下:

 

這個操作界面簡單易用,支持正弦波,方波和三角波的切換,支持占空比設置,支持幅值設置,同時也支持頻率設置,限制頻率範圍1Hz到50KHz。超過50KHz的話,波形效果會變的越來越差。

關於這個對話框的代碼實現就不在教程裡面做講解了,我們這裡主要講解下對話框上的小鍵盤實現。這裡小鍵盤是一個獨立的視窗,父視窗是信號發生器主視窗,通過函數WM_SendMessageNoPara發自定義消息給父視窗,在父視窗裡面更新Graph控制項的波形和波形信息,同時DAC的波形輸出也得到更新。瞭解了這知識點後,再看代碼就比較容易了。

 

知識點拓展:

新版emWin教程第51章:實用的官方小鍵盤實例講解:

http://forum.armfly.com/forum.php?mod=viewthread&tid=19834

另外還有emWin提高篇例子的第一期ATM機裡面也有用到小鍵盤。

http://forum.armfly.com/forum.php?mod=viewthread&tid=23687

 

12.4 信號發生器波形顯示效果

下麵為大家展示信號發生器輸出波形效果:

方波:

 

正弦波:

 

三角波:

 

12.5 總結

本章節為大家講解的信號發生器還是比較實用的,建議實際動手操作下,有興趣的話,還可以進一步優化升級。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為了便於大家學習測試netcore,我們計劃提供1~3台公網Linux伺服器(CentOS/Ubuntu),1vCPU+1G記憶體+100Mbps,為期1年,每周重置系統修改一次密碼 ...
  • 一. 聲明 該節主要介紹SignalR的一些理論知識,代碼量很小,在後續章節編寫中,會不斷回來更新該節,完善該節的介紹;待該系列結束時,該節會和目錄章節合併。 下麵的理論介紹相對枯燥,但對於後面的理解有一定意義,不感興趣的朋友可以右上角離開了,從下一節開始,正式開始擼代碼。 原計劃三天更新一篇,結果 ...
  • 轉自:https://www.cnblogs.com/qianyuliang/p/6501531.html postconf -n #別名資料庫(postalias/newaliases 命令生成) alias_database = hash:/etc/postfix/aliases #別名錶,列出 ...
  • 沒錯,又是我,這期是講解在電腦上如何安裝雙系統,雙系統我想大家都知道是什麼回事吧?就是說一臺電腦里具備兩個系統,但是只能運行其中一種系統,若想運行另一種系統,首先把現在運行的系統關閉後重啟,然後選擇進入到另外一個系統,但是兩者的本地磁碟是通用的,但是必須是文件系統格式相同才行,否則如windows與 ...
  • Iptables採用了表和鏈的分層結構,每個規則表相當於內核空間的一個容器,根據規則集的不同用途劃分為預設的四個表,raw表,mangle表,nat表,filter表,每個表容器內包括不同的規則鏈,根據處理數據包的不同時機劃分為五種鏈,而決定是否過濾或處理數據包的各種規則,按先後順序存放在各規則鏈中 ...
  • Linux 安裝Redis<單機版>(使用Mac遠程訪問) ...
  • linux 重定向及部分命令 一,重定向講解: 1> 標準輸出重定向 覆蓋原有內容 慎用!!!!!! 1>> 標準輸出追加重定向 追加內容 2> 錯誤輸出重定向 只輸出錯誤信息 2>> 錯誤輸出追加重定向 追加內容 0< 標準輸入重定向 0<< 標準輸出追加重定向 xargs 將信息進行分組命令 p ...
  • Linux 安裝Redis<準備>(使用Mac遠程訪問) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...