天天看點

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

完整教程下載下傳位址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第76章       STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

本章節為大家講解FMC總線驅動數模轉換器AD7606,實戰性較強。

76.1 初學者重要提示

76.2 ADC結構分類

76.3 AD7606硬體設計

76.4 AD7606關鍵知識點整理(重要)

76.5 AD7606的FMC接口硬體設計

76.6 AD7606的FMC接口驅動設計

76.7 AD7606闆級支援包(bsp_fmc_ad7606)

76.8 J-Scope實時展示AD7606采集資料說明

76.9 AD7606驅動移植和使用

76.10 實驗例程設計架構

76.11 實驗例程說明(MDK)

76.12 實驗例程說明(IAR)

76.13 總結

  1.   學習本章節前,務必優先學習第47章,了解FMC總線的基礎知識。
  2.   AD7606 的配置很簡單,它沒有内部寄存器,量程範圍和過采樣參數是通過外部IO控制的,采樣速率由MCU或DSP提供的脈沖頻率控制。
  3.   AD7606必須使用單5V供電。而AD7606和MCU之間的通信接口電平由VIO(VDRIVE)引腳控制。也就是說VIO必須接單片機的電源,可以是3.3V也可以是5V(範圍2.3V – 5V)。
  4.   正确的了解過采樣,比如我們設定是1Ksps采樣率,64倍過采樣。意思是指每次采樣,AD7606會采樣64次資料并求平均,相當于AD7606以64Ksps進行采樣的,隻是将每64個采樣點的值做了平均,使用者得到的值就是平均後的數值。是以,如果使用AD7606最高的200Ksps采樣率,就不可以使用過采樣了。
  5.   STM32H7驅動AD7606配合J-Scope實時輸出,效果絕了,堪比示波器http://www.armbbs.cn/forum.php?mod=viewthread&tid=97393 。使用方法詳解本章節77.8小節。
  6.   本章配套例子的序列槽資料展示推薦使用SecureCRT,因為資料展示做了特别處理,友善采集資料在序列槽軟體同一個位置不斷重新整理。
  7.   AD7606資料手冊,子產品原理圖(通用版)和接線圖都已經放到本章教程配置例子的Doc檔案裡。
  8.   ADC 的專業術語诠釋文檔,推薦大家看看:http://www.armbbs.cn/forum.php?mod=viewthread&tid=89414 。
  9.   測試本章配套例子前重要提示:
    •   測試時,務必使用外置電源為開發闆供電,因為AD7606需要5V供電電壓。闆子上插入AD7606子產品時,注意對齊。
    •   闆子上電後,預設是軟體定時采集,0.5秒一次,适合序列槽展示資料。
    •   如果需要使用J-Scope實時展示采集的波形效果,需要按下K2按鍵切換到FIFO模式。
    •   如果使用的JLINK速度不夠快,導緻J-Scope無法最高速度實時上傳,可以使用搖杆上下鍵設定過采樣來降低上傳速度。
    •   預設情況下,程式僅上傳了AD7606通道1采集的資料。

這裡将六種DAC結構為大家做個普及。注,這些知識翻譯自美信和TI的英文技術手冊。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.2.1 SAR ADC(逐次逼近型)

逐次逼近型ADC通常是中高分辨率的首選架構,采樣速率通常低于5Msps。SAR ADC最常見的分辨率範圍是8位到20位,并具有低功耗和小尺寸的特點。這種組合使其非常适合各種應用,例如自動測試裝置,電池供電的裝置,資料采集系統,醫療儀器,電機和過程控制,工業自動化,電信,測試和測量,便攜式系統,高速閉環系統和窄帶接收器。

76.2.2 Sigma-Delta ADC

Sigma-delta ADC主要用于低速應用中,該應用需要通過過采樣來權衡速度和分辨率,然後進行濾波以降低噪聲。24位sigma-delta轉換器用于自動化測試裝置,高精度便攜式傳感器,醫療和科學儀器以及地震資料采集等應用中。

76.2.3 Integrating ADC

內建ADC提供高分辨率,并且可以提供良好的線路頻率和噪聲抑制。內建架構提供了一種新穎且直接的方法,可将低帶寬模拟信号轉換為數字表示形式。這些類型的轉換器通常包括用于LCD或LED顯示器的内置驅動器,并且在許多便攜式儀器應用中都可以找到,包括數字面闆表和數字萬用表。

76.2.4 FLASH ADC

Flash ADC是将模拟信号轉換為數字信号的最快方法。它們适用于需要非常大帶寬的應用。然而,閃存轉換器功率高,具有相對較低的分辨率,并且可能非常昂貴。這将它們限制在通常無法以其他任何方式解決的高頻應用中。示例包括資料采集,衛星通信,雷達處理,示波器和高密度磁盤驅動器。

76.2.5 Pipelined ADC

流水線ADC已成為最受歡迎的ADC體系結構,其采樣率從每秒幾兆采樣(MS / s)到最高100MS / s +,分辨率為8至16位。它們提供的分辨率和采樣率,可覆寫各種應用,包括CCD成像,超聲醫學成像,數字接收器,基站,數字視訊(例如HDTV),xDSL,電纜數據機和快速以太網。

76.2.6 Two Step ADC

兩步ADC也稱為子範圍轉換器,有時也稱為多步或half flash(比Flash架構慢)。這是Flash ADC和流水線ADC的交叉點。與Flash ADC相比,可以實作更高的分辨率或更小的裸片尺寸。

這裡将開發闆上的AD7606硬體接口,普通型AD7606子產品,屏蔽型AD7606子產品和磁耦高速隔離型AD7606子產品為大家做個說明。

AD7606的原理圖下載下傳:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=97547 。

76.3.1 AD7606硬體接口

V7闆子上AD7606子產品的插座的原理圖如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

實際對應開發闆的位置如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

為了友善大家更好的了解接線,下面是框圖:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

子產品引腳說明:

  •   OS2 OS1 OS2 :

組合狀态選擇過采樣模式。

    •   000表示無過采樣,最大200Ksps采樣速率。
    •   001表示2倍過采樣, 也就是硬體内部采集2個樣本求平均。
    •   010表示4倍過采樣, 也就是硬體内部采集4個樣本求平均。
    •   011表示8倍過采樣, 也就是硬體内部采集8個樣本求平均。
    •   100表示16倍過采樣, 也就是硬體内部采集16個樣本求平均。
    •   101表示32倍過采樣, 也就是硬體内部采集32個樣本求平均。
    •   110表示64倍過采樣, 也就是硬體内部采集64個樣本求平均。

過采樣倍率越高,ADC轉換時間越長,可得到的最大采樣頻率就越低。

  •   CVA,CVB :

啟動AD轉換的控制信号。CVA決定1-4通道,CVB決定5-8通道。2個信号可以錯開短暫的時間。一般情況可以将CVA,CVB并聯在一起。

  •   RAGE :

量程範圍選擇。0表示正負5V, 1表示正負10V。

  •  RD :

讀信号。

  •   RST :

複位信号。

  •   BUSY :

忙信号。

  •   CS :

片選信号。

  •   FRST :

第1個通道樣本的訓示信号。【注,此引腳可以省略不使用】

  •   VIO :

通信接口電平。

  •   DB0-DB15 :

資料總線。

如果采用SPI接口方式,接線框圖如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.3.2 AD7606子產品(通用版)

産品規格:

1、 16bit分辨率,内置基準,單5V供電。

2、 8路模拟輸入,阻抗1M歐姆。【無需負電源,無需前端模拟運放電路,可直接接傳感器輸出】

3、 輸入範圍可以選擇正負5V或者正負10V,可通過IO控制量程。

4、 最大采樣頻率 200Ksps,支援8檔過采樣設定(可以有效降低抖動)。

5、 通信接口支援SPI或16位總線方式(也支援8位總線,一般用的比較少),接口IO電平可以是5V或3.3V。

重要提示:

1、 AD7606的配置很簡單,它沒有内部寄存器。量程範圍和過采樣參數是通過外部IO控制的。采樣速率由MCU或DSP提供的脈沖頻率控制。

2、 AD7606必須使用單5V供電。

3、 AD7606和MCU之間的通信接口電平由VIO(VDRIVE)引腳控制。也就是說VIO必須接單片機的電源,可以是3.3V也可以是5V。

産品效果:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

8080或者SPI接口方式選擇

出廠的AD7606子產品預設是8080并行接口,如果用SPI接口模式,需要修改R1、R2電阻配置。

并口模式跳線:R1 懸空(不貼),R2貼10K電阻。

SPI接口模式跳線:R1 貼10K電阻,R2 懸空(不貼)。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.3.3 AD7606子產品(屏蔽版)

屏蔽版主要是為了更好的應對複雜的電磁工作,軟體代碼與非屏蔽版是一樣的:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.3.4 AD7606子產品(磁耦高速隔離)

該款ADC子產品采用磁耦隔離技術隔離SPI通信接口,采用DC-DC隔離電源子產品隔離供電電源。高速SPI接口,ADC主晶片采用AD7606晶片。8通道200KHz采樣。量程和濾波設定通過短路焊點設定。

産品規格

模拟通道 : 8路同步采集。

采樣頻率 : 最大200KHz。

ADC分辨率 : 16bit。

輸入量程 : 正負5V或正負10V (通過焊點切換)。

濾波設定 : 0 - 64 共7級硬體均值濾波。

供電電壓 : 5.0V,  耗電最大50mA。

通信接口 : SPI,最大時鐘頻率 16MHz。

接口電平 : 3.3V 或 5V  (3.3V時,耗電15mA)。

産品特點

1、電源隔離,隔離電壓1500V。

2、SPI通信接口隔離,高速磁耦隔離技術。

3、短路點切換量程和過采樣(濾波)參數。

4、體積小,2.0mm間距排針,節約主機闆面積。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

引腳定義和接線圖:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

驅動AD7606需要對下面這些知識點有個認識。

76.4.1 AD7606基礎資訊

  •   支援8通道同步采樣,每個通道最高200Ksps,16bit分辨率。
  •   真雙極模拟輸入範圍:±10V、±5V。
  •   5V單模拟電源,VDRIVER支援2.3V到5V。
  •   完全內建的資料采集解決方案:
    •   模拟輸入鉗位保護,可以耐受±16.5V的電壓。
    •   具有1MΩ模拟輸入阻抗的輸入緩沖器。
    •   二階抗混疊模拟濾波器。
    •  片内精密基準電壓及緩沖。
    •   通過數字濾波器,提供過采樣功能。
  •   靈活的并行/串行即可,支援SPI/QSPI/MICROWIRE/DSP等。
  •   性能
    •   模拟輸入通道提供7KV ESD。
    •   95.5dB SNR,-107dB THD,±0.5 LSB INL,±0.5 LSB DNL。
    •   低功耗:100mW。
    •   待機功耗:25mW。
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.4.2 AD7606常用引腳的作用

AD7606的封裝形式:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

這裡把常用的幾個引腳做個說明:

  •   AVcc

模拟電源電壓,4.75V到5.25V。這是内部前端放大器和ADC核心的電源電壓。應将這些電壓引腳去偶接AGND。

  •   AGND

模拟地,這些引腳是AD7606上所有模拟電路的接地基準點。所有模拟輸入信号和外部基準信号都應參考這些引腳。

  •   CONVSTA,CONVSTB :

啟動AD轉換的控制信号。CONVSTA決定1-4通道,CONVSTB決定5-8通道。2個信号可以錯開短暫的時間。一般情況可以将CVA,CVB并聯在一起。

量程範圍選擇。0表示正負5V, 1表示正負10V.

  •  RD /SCL:

讀信号,低電平有效。

  •   RESET

CONVST A和CONVST B均達到上升沿後,此引腳變為邏輯高電平,表示轉換過程已經開始,BUSY輸出保持高電平,直到所有通道的轉換過程完成為止。BUSY下降沿表示轉換資料正被鎖存至輸出資料寄存器,此時使用者就可以讀取資料。

片選信号,低電平有效。

  •   VDriver:
  •   REF SELECT

内部/外部基準電壓選擇。如果設定此引腳設為邏輯高電平,使用内部基準電壓。如果此引腳設為邏輯低電平,則内部基準電壓禁止,必須将外部基準電壓加到REFIN/REFOUT引腳。

  •   REFIN/REFOUT

基準電壓輸入(REFIN)/基準電壓輸出(REFOUT)引腳,如果REF SELECT引腳設定為邏輯高電平,此引腳将提供2.5V片内基準電壓供外部使用。或者可以将REF SELECT引腳設定為邏輯低電平将禁止用内部基準電壓。

  •   V1到V8

模拟輸入,此引腳為單端模拟輸入,此通道的模拟輸入範圍由RANGE引腳決定。

  •   V1GND到V8GND

模拟輸入接地引腳,這些引腳與模拟輸入引腳V1到V8對應,所有模拟輸入AGND引腳都應連接配接到系統的AGND平面。

76.4.3 AD7606輸出電壓計算公式

AD7606的計算公式如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

采用二進制補碼(其實就是16bit有符号數,将轉換結果定義為int16_t即可),因為AD7606支援正負壓采集。

  •   VIN

AD7606采集到的電壓值範圍-32768到32767。

  •   REF

一般使用内部基準,即2.5V。

76.4.4 AD7606時序圖

了解時序參數是驅動AD7606能否成功的關鍵,我們這裡對幾個重要的參數做個說明。

1、AD7606的CONVST轉換時序(轉換之後讀取資料):

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
  •   t5

CONVST A和CONVST B上升沿之間最大允許的延遲時間。一般我們是用一根控制線同時控制CONVST A和CONVST B,是以可以不用管這個時間。

  •   tCYCLE

并行模式,轉換後并讀取資料的最大值是5us,即最高支援的時脈速度是20MHz及其以上。

  •   tCONV

轉換時間。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
  •   t3

最短的CONVST A/B電平脈沖,最小值25ns。

  •   t4

BUSY下降沿到CS下降沿設定時間,最小值0ns,是以可以忽略。

2、AD7606的并行驅動模式有兩種時序圖,一個是獨立的CS片選和RD讀信号時序圖:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
  •   t8

CS到RD的設定時間,最小值是0ns,可以忽略。

  •   t10

RD讀信号的低電平脈沖寬度,通信電壓不同,時間不同。對于STM32來說,FMC通信電平一般是3.3V,即最小值21ns。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
  •   t11

RD高電平脈沖寬度,最小值15ns。

  •   t9

CS到RD保持時間,最小值0ns,可以忽略。

  •   13到t17 

這幾個參數了解下即可:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

3、另一個是CS片選和RD相連的方式:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

這個時序裡面最重要的是t12。

  •   t12

CS和RD的高電平脈沖寬度,最小值22ns。

第2個和第3個時序圖的主要差別是連續讀取8路資料時,一個CS信号是全程低電平,另一個CS信号是與RD信号同步,每讀取完一路,拉高一次。

76.4.5 AD7606的過采樣

使用過采樣可以改善SNR信噪比。SNR性能随着過采樣倍率提高而改善,具體參數如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

通過這個表,我們可以友善的了解不同過采樣下的信噪比,3dB帶寬時的頻率和最高支援的采樣率。

注意正确的了解過采樣,比如我們設定是1Ksps采樣率,64倍過采樣。意思是指每次采樣,AD7606會采樣64次資料并求平均,相當于AD7606以64Ksps進行采樣的,隻是将每64個采樣點的值做了平均,使用者得到的值就是平均後的數值。是以,如果使用AD7606最高的200Ksps采樣率,就不可以使用過采樣了。

FMC硬體接口涉及到的知識點稍多,下面逐一為大家做個說明。

76.5.1 FMC的塊區配置設定

FMC總線可操作的位址範圍0x60000000到0xDFFFFFFF,具體的框圖如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

從上面的框圖可以看出,NOR/PSRAM/SRAM塊區有4個片選NE1,NE2,NE3和NE4,但由于引腳複用,部分片選對應的引腳要用于其他功能,而且要控制的總線外設較多,導緻片選不夠用。是以需要增加譯碼器。

76.5.2 譯碼器及其位址計算

有了前面的認識之後再來看下面的譯碼器電路:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

SN74LVC1G139APWR是雙2-4線位址譯碼器,也就是帶了兩個譯碼器。原理圖上僅用了一個。下面是139的真值表和引腳功能:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

通過上面的原理圖和真值表就比較好了解了,真值表的輸出是由片選FMC_NE1和位址線FMC_A10、FMC_A11控制。

FMC_NE1 輸出低電平:

  •   FMC_A11(B),FMC_A10(A) = 00時,1Y0輸出的低電平,選擇的是OLED。
  •   FMC_A11(B),FMC_A10(A) = 01時,1Y1輸出的低電平,選擇的是74HC574。
  •   FMC_A11(B),FMC_A10(A) = 10時,1Y2輸出的低電平,選擇的是DM9000。
  •   FMC_A11(B),FMC_A10(A) = 11時,1Y3輸出的低電平,選擇的是AD7606。

然後我們再計算譯碼器的位址,注意,這裡位址的計算都是按照FMC的32bit通路模式計算的,因為我們的V7程式中是将NE1對應的FMC配置為32bit模式了。

具體FMC的32bit通路模式,16bit通路模式和8bit通路模式的差別在第47章的2.4小節有詳細講解。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

32bit模式下,我們計算A10和A11的時候,實際上需要按HADDR12和HADDR13計算的。

如果來算NE1 + HADDR12 + HADDR13的四種組合位址就是如下:

NE1 + HADDR13 + HADDR12 = 0x60000000 +  0<<13 + 0<<12 = 0x60000000

NE1 + HADDR13 + HADDR12 = 0x60000000 +  0<<13 + 1<<12 = 0x60001000

NE1 + HADDR13 + HADDR12 = 0x60000000 +  1<<13 + 0<<12 = 0x60002000

NE1 + HADDR13 + HADDR12 = 0x60000000 +  1<<13 + 1<<12 = 0x60003000

這樣一來,原理圖裡面給的位址就對應上了。同理如果配置為16位模式和8位模式,大家應該也都會計算了。

AD7606的程式驅動架構設計如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

有了這個框圖,程式設計就比較好了解了。

76.6.1 第1步,AD7606整體驅動架構設計

主要實作了兩種采集方式:

(1)軟體定時擷取方式,适合低速查詢擷取。

(2)FIFO工作模式,适合8路實時采集,支援最高采樣率200Ksps。

  •   方案一:軟體定時擷取方式代碼架構:

可以在硬體定時器中斷服務程式或者軟體定時器裡面實作。

定時器中斷ISR:
{
    中斷入口;
讀取8個通道的采樣結果儲存到RAM;  ----> 讀取的是上次的采集結果,對于連續采集來說,是沒有關系的
    啟動下次ADC采集;(翻轉CVA和CVB)
    中斷傳回;
}      

定時器的頻率就是ADC采樣頻率。這種模式可以不連接配接BUSY口線。

  •   方案二:FIFO工作模式架構:

    配置CVA、CVB引腳為PWM輸出模式,周期設定為需要的采樣頻率,之後MCU将産生周期非常穩定的AD轉換信号

    将BUSY口線設定為中斷下降沿觸發模式;

外部中斷ISR:
{
    中斷入口;
    讀取8個通道的采樣結果儲存到RAM;
}      
  •   方案1和方案2的差異

(1)方案1 可以少用 BUSY口線,但是其他中斷服務程式或者主程式臨時關閉全局中斷時,可能導緻ADC轉換周期存在輕微抖動。

(2)方案2 可以確定采集時鐘的穩定性,因為它是MCU硬體産生的,但是需要多接一根BUSY口線。

76.6.2 第2步,AD7606所涉及到的GPIO配置

這裡需要把用到的GPIO時鐘、FMC時鐘、GPIO引腳和複用配置好即可:

/*
*********************************************************************************************************
*    函 數 名: AD7606_CtrlLinesConfig
*    功能說明: 配置GPIO口線,FMC管腳設定為複用功能
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
/*
    安富萊STM32-H7開發闆接線方法:4片74HC574挂在FMC 32位總線上。1個位址端口可以擴充出32個IO
    PD0/FMC_D2
    PD1/FMC_D3
    PD4/FMC_NOE        ---- 讀控制信号,OE = Output Enable , N 表示低有效
    PD5/FMC_NWE        -XX- 寫控制信号,AD7606 隻有讀,無寫信号
    PD8/FMC_D13
    PD9/FMC_D14
    PD10/FMC_D15
    PD14/FMC_D0
    PD15/FMC_D1

    PE7/FMC_D4
    PE8/FMC_D5
    PE9/FMC_D6
    PE10/FMC_D7
    PE11/FMC_D8
    PE12/FMC_D9
    PE13/FMC_D10
    PE14/FMC_D11
    PE15/FMC_D12
    
    PG0/FMC_A10        --- 和主片選FMC_NE2一起譯碼
    PG1/FMC_A11        --- 和主片選FMC_NE2一起譯碼
    PD7/FMC_NE1        --- 主片選(OLED, 74HC574, DM9000, AD7606)    

     +-------------------+------------------+
     +   32-bits Mode: D31-D16              +
     +-------------------+------------------+
     | PH8 <-> FMC_D16   | PI0 <-> FMC_D24  |
     | PH9 <-> FMC_D17   | PI1 <-> FMC_D25  |
     | PH10 <-> FMC_D18  | PI2 <-> FMC_D26  |
     | PH11 <-> FMC_D19  | PI3 <-> FMC_D27  |
     | PH12 <-> FMC_D20  | PI6 <-> FMC_D28  |
     | PH13 <-> FMC_D21  | PI7 <-> FMC_D29  |
     | PH14 <-> FMC_D22  | PI9 <-> FMC_D30  |
     | PH15 <-> FMC_D23  | PI10 <-> FMC_D31 |
     +------------------+-------------------+
*/

/* 
    控制AD7606參數的其他IO配置設定在擴充的74HC574上
    X13 - AD7606_OS0
    X14 - AD7606_OS1
    X15 - AD7606_OS2
    X24 - AD7606_RESET
    X25 - AD7606_RAGE    
    
    PE5 - AD7606_BUSY
*/
static void AD7606_CtrlLinesConfig(void)
{
    /* bsp_fm_io 已配置fmc,bsp_InitExtIO();
       此處可以不必重複配置 
    */

    GPIO_InitTypeDef gpio_init_structure;

    /* 使能 GPIO時鐘 */
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOI_CLK_ENABLE();

    /* 使能FMC時鐘 */
    __HAL_RCC_FMC_CLK_ENABLE();

    /* 設定 GPIOD 相關的IO為複用推挽輸出 */
    gpio_init_structure.Mode = GPIO_MODE_AF_PP;
    gpio_init_structure.Pull = GPIO_PULLUP;
    gpio_init_structure.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init_structure.Alternate = GPIO_AF12_FMC;
    
    /* 配置GPIOD */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 |
                                GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 |
                                GPIO_PIN_15;
    HAL_GPIO_Init(GPIOD, &gpio_init_structure);

    /* 配置GPIOE */
    gpio_init_structure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |
                                GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |
                                GPIO_PIN_15;
    HAL_GPIO_Init(GPIOE, &gpio_init_structure);

    /* 配置GPIOG */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    HAL_GPIO_Init(GPIOG, &gpio_init_structure);
    
    /* 配置GPIOH */
    gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12
                        | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &gpio_init_structure);

    /* 配置GPIOI */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6
                        | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &gpio_init_structure);
    
    
    /* 配置BUSY引腳,預設是普通IO狀态 */
    {
        GPIO_InitTypeDef   GPIO_InitStructure;
    
        __HAL_RCC_SYSCFG_CLK_ENABLE();
                
        BUSY_RCC_GPIO_CLK_ENABLE();        /* 打開GPIO時鐘 */

        /* BUSY信号,使用的PE5,用于轉換完畢檢測 */
        GPIO_InitStructure.Mode = GPIO_MODE_INPUT;   /* 設定推挽輸出 */
        GPIO_InitStructure.Pull = GPIO_NOPULL;       /* 無上拉下拉 */
        GPIO_InitStructure.Pin = BUSY_PIN;           
        HAL_GPIO_Init(BUSY_GPIO, &GPIO_InitStructure);    
    }
    
    /* CONVST 啟動ADC轉換的GPIO = PC6 */
    {
        GPIO_InitTypeDef   GPIO_InitStructure;
        CONVST_RCC_GPIO_CLK_ENABLE();

        /* 配置PC6 */
        GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;        /* 設定推挽輸出 */
        GPIO_InitStructure.Pull = GPIO_NOPULL;            /* 上下拉電阻不使能 */
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;  /* GPIO速度等級 */    

        GPIO_InitStructure.Pin = CONVST_PIN;    
        HAL_GPIO_Init(CONVST_GPIO, &GPIO_InitStructure);    
    }
}      

這裡重點注意AD7606_CONVST和AD7606_BUSY引腳,上電後的預設配置是普通IO。另外還有過采樣的3個引腳,量程配置的1個引腳和複位控制的1個引腳,均通過V7闆子的擴充IO實作:

/* 設定過采樣的IO, 在擴充的74HC574上 */
#define OS0_1()        HC574_SetPin(AD7606_OS0, 1)
#define OS0_0()        HC574_SetPin(AD7606_OS0, 0)
#define OS1_1()        HC574_SetPin(AD7606_OS1, 1)
#define OS1_0()        HC574_SetPin(AD7606_OS1, 0)
#define OS2_1()        HC574_SetPin(AD7606_OS2, 1)
#define OS2_0()        HC574_SetPin(AD7606_OS2, 0)

/* 設定輸入量程的GPIO, 在擴充的74HC574上 */
#define RANGE_1()    HC574_SetPin(AD7606_RANGE, 1)
#define RANGE_0()    HC574_SetPin(AD7606_RANGE, 0)

/* AD7606複位口線, 在擴充的74HC574上 */
#define RESET_1()    HC574_SetPin(AD7606_RESET, 1)
#define RESET_0()    HC574_SetPin(AD7606_RESET, 0)      

76.6.3 第3步,FMC的時鐘源選擇

使用FMC可以選擇如下幾種時鐘源HCLK3,PLL1Q,PLL2R和PER_CK:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

我們這裡直接使用HCLK3,配置STM32H7的主頻為400MHz的時候,HCLK3輸出的200MHz,這個速度是FMC支援的最高時鐘,正好用于這裡:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.6.4 第4步,FMC的時序配置(重要)

由于操作AD7606僅需要讀操作,而且使用的是FMC總線的Mode_A,那麼僅需按照如下時序圖配置好即可:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

根據這個時序圖,重點配置好ADDSET位址建立時間和DATAST資料建立時間即可。

  •   DATAST(DataSetupTime,資料建立時間)

DATAST實際上對應的就是76.4.4小節裡面的t10 。RD讀信号的低電平脈沖寬度,通信電壓不同,時間不同,對于STM32來說,FMC通信電平一般是3.3V,即最小值21ns。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
  •   ADDST(AddressSetupTime,位址建立時間)

DATAST實際上對應的就是76.4.4小節裡面的t11 或者t12。

    •   如果采用CS(NEx)片選和RD(NOE)讀信号獨立方式,對應的時間最小15ns,即t11 。
    •   如果采用CS(NEx)片選和RD(NOE)讀信号并聯方式,對應的時間最小22ns,即t12  。

我們這裡将t12作為最小值更合理,因為CS(NEx)片選信号,每讀取完畢一路,拉高一次。

有了這些認識後,再來看FMC的時序配置就比較好了解了:

1.    /*
2.    ******************************************************************************************************
3.    *    函 數 名: AD7606_FSMCConfig
4.    *    功能說明: 配置FSMC并口通路時序
5.    *    形    參: 無
6.    *    返 回 值: 無
7.    ******************************************************************************************************
8.    */
9.    static void AD7606_FSMCConfig(void)
10.    {
11.        /* 
12.           DM9000,擴充IO,OLED和AD7606公用一個FMC配置,如果都開啟,請以FMC速度最慢的為準。
13.           進而保證所有外設都可以正常工作。
14.        */
15.        SRAM_HandleTypeDef hsram = {0};
16.        FMC_NORSRAM_TimingTypeDef SRAM_Timing = {0};
17.            
18.       /*
19.        AD7606規格書要求(3.3V時,通信電平Vdriver):RD讀信号低電平脈沖寬度最短21ns,對應DataSetupTime
20.        CS片選和RD讀信号獨立方式的高電平脈沖最短寬度15ns。
21.        CS片選和RD讀信号并聯方式的高電平脈沖最短寬度22ns。
22.        這裡将22ns作為最小值更合理些,對應FMC的AddressSetupTime。
23.        
24.            5-x-5-x-x-x  : RD高持續25ns, 低電平持續25ns. 讀取8路樣本資料到記憶體差不多就是400ns。
25.        */
26.        hsram.Instance  = FMC_NORSRAM_DEVICE;
27.        hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;
28.        
29.        /* FMC使用的HCLK3,主頻200MHz,1個FMC時鐘周期就是5ns */
30.        SRAM_Timing.AddressSetupTime       = 5; /* 5*5ns=25ns,位址建立時間,範圍0 -15個FMC時鐘周期個數 */
31.        SRAM_Timing.AddressHoldTime        = 2; /* 位址保持時間,配置為模式A時,用不到此參數 範圍1 -15個
32.                                                    時鐘周期個數 */
33.        SRAM_Timing.DataSetupTime          = 5;  /* 5*5ns=25ns,資料建立時間,範圍1 -255個時鐘周期個數 */
34.        SRAM_Timing.BusTurnAroundDuration  = 1;  /* 此配置用不到這個參數 */
35.        SRAM_Timing.CLKDivision            = 2;  /* 此配置用不到這個參數 */
36.        SRAM_Timing.DataLatency            = 2;  /* 此配置用不到這個參數 */
37.        SRAM_Timing.AccessMode             = FMC_ACCESS_MODE_A; /* 配置為模式A */
38.        hsram.Init.NSBank             = FMC_NORSRAM_BANK1;              /* 使用的BANK1,即使用的片選
39.                                                                            FMC_NE1 */
40.        hsram.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;   /* 禁止位址資料複用 */
41.        hsram.Init.MemoryType         = FMC_MEMORY_TYPE_SRAM;           /* 存儲器類型SRAM */
42.        hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_32;   /* 32位總線寬度 */
43.        hsram.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;  /* 關閉突發模式 */
44.        hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;   /* 用于設定等待信号的極性,關閉突
45.                                                                            發模式,此參數無效 */
46.        hsram.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;      /* 關閉突發模式,此參數無效 */
47.        hsram.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;     /* 用于使能或者禁止寫保護 */
48.        hsram.Init.WaitSignal         = FMC_WAIT_SIGNAL_DISABLE;        /* 關閉突發模式,此參數無效 */
49.        hsram.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;      /* 禁止擴充模式 */
50.        hsram.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 用于異步傳輸期間,使能或者禁止
51.                                                                            等待信号,這裡選擇關閉 */
52.        hsram.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;        /* 禁止寫突發 */
53.        hsram.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 僅同步模式才做時鐘輸出 */
54.        hsram.Init.WriteFifo          = FMC_WRITE_FIFO_ENABLE;          /* 使能寫FIFO */
55.    
56.        /* 初始化SRAM控制器 */
57.        if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)
58.        {
59.            /* 初始化錯誤 */
60.            Error_Handler(__FILE__, __LINE__);
61.        }    
62.    }      

這裡把幾個關鍵的地方闡釋下:

  •   第15- 16行,對作為局部變量的HAL庫結構體做初始化,防止不确定值配置時出問題。
  •   第30行,位址建立時間,對于AD7606來說,這個地方最小值22ns。保險起見,這裡取值5個FMC時鐘周期,即25ns。
  •   第31行,位址保持時間,對于FMC模式A來說,此參數用不到。
  •   第33行,資料建立時間,對于AD7606來說,這個地方最小值是21ns,保險起見,這裡取值5個FMC時鐘周期,即25ns。
  •   第34 – 36行,目前配置用不到這三個參數。
  •   第38行,使用的BANK1,即使用的片選FMC_NE1。

76.6.5 第5步,FMC的MPU配置

實際測試發現,使能FMC_NE1所管理的存儲區的Cache功能後,會出現擴充IO的NE片選和NWE信号輸出2次的問題。經過各種Cache方式配置、FMC帶寬配置、操作FMC時的資料位寬設定,發現禁止了Cache功能就正常了,也就是說,設定FMC_NE1所管理的存儲區MPU屬性為Device或者Strongly Ordered即可。

/* 配置FMC擴充IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);      

MPU配置中直接從FMC_NE1的首位址開始配置,設定了64KB空間的屬性。将FMC_NE1通過譯碼器所管理的所有裝置位址全部設定為此配置:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.6.6 第6步,AD7606的軟體定時器讀取資料(方案一)

AD7606的軟體定時器讀取方式比較簡單,周期性調用下面兩個函數即可:

AD7606_ReadNowAdc();        /* 讀取采樣結果 */
AD7606_StartConvst();        /* 啟動下次轉換 */      

函數AD7606_ReadNowAdc的實作如下:

/* AD7606 FSMC總線位址,隻能讀,無需寫 */
#define AD7606_RESULT()    *(__IO uint16_t *)0x60003000

void AD7606_ReadNowAdc(void)
{
    g_tAD7606.sNowAdc[0] = AD7606_RESULT();    /* 讀第1路樣本 */
    g_tAD7606.sNowAdc[1] = AD7606_RESULT();    /* 讀第2路樣本 */
    g_tAD7606.sNowAdc[2] = AD7606_RESULT();    /* 讀第3路樣本 */
    g_tAD7606.sNowAdc[3] = AD7606_RESULT();    /* 讀第4路樣本 */
    g_tAD7606.sNowAdc[4] = AD7606_RESULT();    /* 讀第5路樣本 */
    g_tAD7606.sNowAdc[5] = AD7606_RESULT();    /* 讀第6路樣本 */
    g_tAD7606.sNowAdc[6] = AD7606_RESULT();    /* 讀第7路樣本 */
    g_tAD7606.sNowAdc[7] = AD7606_RESULT();    /* 讀第8路樣本 */

    AD7606_SEGGER_RTTOUT();
}      

啟動ADC轉換的函數實作如下:

/*
*********************************************************************************************************
*    函 數 名: AD7606_StartConvst
*    功能說明: 啟動1次ADC轉換
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_StartConvst(void)
{
    /* page 7:  CONVST 高電平脈沖寬度和低電平脈沖寬度最短 25ns */
    /* CONVST平時為高 */
    CONVST_0();
    CONVST_0();
    CONVST_0();

    CONVST_1();
}      

76.6.7 第7步,AD7606的FIFO方式實時讀取資料(方案二)

通過下面的框圖可以對AD7606的FIFO方式有個整體認識:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
  •   啟動采集函數AD7606_StartRecord

這個函數的主要作用是配置TIM8的CH1 PWM輸出并使能BUSY引腳的EXTI中斷。

/*
*********************************************************************************************************
*    函 數 名: AD7606_StartRecord
*    功能說明: 開始采集
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_StartRecord(uint32_t _ulFreq)
{
    AD7606_StopRecord();

    AD7606_Reset();            /* 複位硬體 */
    AD7606_StartConvst();        /* 啟動采樣,避免第1組資料全0的問題 */

    g_tAdcFifo.usRead = 0;        /* 必須在開啟定時器之前清0 */
    g_tAdcFifo.usWrite = 0;
    g_tAdcFifo.usCount = 0;
    g_tAdcFifo.ucFull = 0;

    AD7606_EnterAutoMode(_ulFreq);
}
/*
*********************************************************************************************************
*    函 數 名: AD7606_EnterAutoMode
*    功能說明: 配置硬體工作在自動采集模式,結果存儲在FIFO緩沖區。
*    形    參:  _ulFreq : 采樣頻率,機關Hz,    1k,2k,5k,10k,20K,50k,100k,200k
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_EnterAutoMode(uint32_t _ulFreq)
{
    /* 配置PC6為TIM8_CH1功能,輸出占空比50%的方波 */
    bsp_SetTIMOutPWM(CONVST_GPIO, CONVST_PIN, CONVST_TIMX,  CONVST_TIMCH, _ulFreq, 5000);
    
    /* 配置PE5, BUSY 作為中斷輸入口,下降沿觸發 */
    {
        GPIO_InitTypeDef   GPIO_InitStructure;
    
        CONVST_RCC_GPIO_CLK_ENABLE();    /* 打開GPIO時鐘 */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

        GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;    /* 中斷下降沿觸發 */
        GPIO_InitStructure.Pull = GPIO_NOPULL;
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;        
        GPIO_InitStructure.Pin = BUSY_PIN;
        HAL_GPIO_Init(BUSY_GPIO, &GPIO_InitStructure);    

        HAL_NVIC_SetPriority(BUSY_IRQn, 2, 0);
        HAL_NVIC_EnableIRQ(BUSY_IRQn);    
    }        
}      
  •   AD7606轉換完畢後,中斷服務程式的處理。

下面這幾個函數的調用關系是

    •   EXTI9_5_IRQHandler調用HAL_GPIO_EXTI_IRQHandler。
    •   HAL_GPIO_EXTI_IRQHandler調用HAL_GPIO_EXTI_Callback。
    •   HAL_GPIO_EXTI_Callback調用AD7606_ISR。
    •   AD7606_ISR調用AD7606_ReadNowAdc。
/*
*********************************************************************************************************
*    函 數 名: EXTI9_5_IRQHandler
*    功能說明: 外部中斷服務程式。
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(BUSY_PIN);
}

/*
*********************************************************************************************************
*    函 數 名: EXTI9_5_IRQHandler
*    功能說明: 外部中斷服務程式入口, AD7606_BUSY 下降沿中斷觸發
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == BUSY_PIN)
    {
        AD7606_ISR();
    }
}

/*
*********************************************************************************************************
*    函 數 名: AD7606_ISR
*    功能說明: 定時采集中斷服務程式
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_ISR(void)
{
    uint8_t i;

    AD7606_ReadNowAdc();

    for (i = 0; i < 8; i++)
    {
        g_tAdcFifo.sBuf[g_tAdcFifo.usWrite] = g_tAD7606.sNowAdc[i];
        if (++g_tAdcFifo.usWrite >= ADC_FIFO_SIZE)
        {
            g_tAdcFifo.usWrite = 0;
        }
        if (g_tAdcFifo.usCount < ADC_FIFO_SIZE)
        {
            g_tAdcFifo.usCount++;
        }
        else
        {
            g_tAdcFifo.ucFull = 1;        /* FIFO 滿,主程式來不及處理資料 */
        }
    }
}

/*
*********************************************************************************************************
*    函 數 名: AD7606_ReadNowAdc
*    功能說明: 讀取8路采樣結果。結果存儲在全局變量 g_tAD7606
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_ReadNowAdc(void)
{
    g_tAD7606.sNowAdc[0] = AD7606_RESULT();    /* 讀第1路樣本 */
    g_tAD7606.sNowAdc[1] = AD7606_RESULT();    /* 讀第2路樣本 */
    g_tAD7606.sNowAdc[2] = AD7606_RESULT();    /* 讀第3路樣本 */
    g_tAD7606.sNowAdc[3] = AD7606_RESULT();    /* 讀第4路樣本 */
    g_tAD7606.sNowAdc[4] = AD7606_RESULT();    /* 讀第5路樣本 */
    g_tAD7606.sNowAdc[5] = AD7606_RESULT();    /* 讀第6路樣本 */
    g_tAD7606.sNowAdc[6] = AD7606_RESULT();    /* 讀第7路樣本 */
    g_tAD7606.sNowAdc[7] = AD7606_RESULT();    /* 讀第8路樣本 */

    AD7606_SEGGER_RTTOUT();
}      

這裡的FIFO比較好了解,與前面按鍵FIFO章節的實作是一樣的,詳情可重溫下按鍵FIFO的實作。

76.6.8 第8步,AD7606的雙緩沖方式存儲思路

為了友善大家實時處理采集的資料,專門預留了一個弱定義函數AD7606_SEGGER_RTTOUT,友善大家将采集函數存儲到雙緩沖裡面,這個函數是在中斷服務程式裡面調用的。

/*
*********************************************************************************************************
*    函 數 名: AD7606_ReadNowAdc
*    功能說明: 讀取8路采樣結果。結果存儲在全局變量 g_tAD7606
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
/* 弱定義,友善使用者将采集的結果實時輸出 */
__weak void AD7606_SEGGER_RTTOUT(void)
{
    
}

void AD7606_ReadNowAdc(void)
{
    g_tAD7606.sNowAdc[0] = AD7606_RESULT();    /* 讀第1路樣本 */
    g_tAD7606.sNowAdc[1] = AD7606_RESULT();    /* 讀第2路樣本 */
    g_tAD7606.sNowAdc[2] = AD7606_RESULT();    /* 讀第3路樣本 */
    g_tAD7606.sNowAdc[3] = AD7606_RESULT();    /* 讀第4路樣本 */
    g_tAD7606.sNowAdc[4] = AD7606_RESULT();    /* 讀第5路樣本 */
    g_tAD7606.sNowAdc[5] = AD7606_RESULT();    /* 讀第6路樣本 */
    g_tAD7606.sNowAdc[6] = AD7606_RESULT();    /* 讀第7路樣本 */
    g_tAD7606.sNowAdc[7] = AD7606_RESULT();    /* 讀第8路樣本 */

    AD7606_SEGGER_RTTOUT();
}      

本章是将此函數用于實時采集資料并輸出到J-Scope。

76.6.9 第9步,AD7606過采樣設定

AD7606的過采樣實作比較簡單,通過IO引腳就可以控制,支援2倍,4倍,8倍,16倍,32倍和64倍過采樣設定。

/*
*********************************************************************************************************
*    函 數 名: AD7606_SetOS
*    功能說明: 配置AD7606數字濾波器,也就設定過采樣倍率。
*              通過設定 AD7606_OS0、OS1、OS2口線的電平組合狀态決定過采樣倍率。
*              啟動AD轉換之後,AD7606内部自動實作剩餘樣本的采集,然後求平均值輸出。
*
*              過采樣倍率越高,轉換時間越長。
*              0、無過采樣時,AD轉換時間 = 3.45us - 4.15us
*              1、2倍過采樣時 = 7.87us - 9.1us
*              2、4倍過采樣時 = 16.05us - 18.8us
*              3、8倍過采樣時 = 33us - 39us
*              4、16倍過采樣時 = 66us - 78us
*              5、32倍過采樣時 = 133us - 158us
*              6、64倍過采樣時 = 257us - 315us
*
*    形    參: _ucOS : 過采樣倍率, 0 - 6
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_SetOS(uint8_t _ucOS)
{
    g_tAD7606.ucOS = _ucOS;
    switch (_ucOS)
    {
        case AD_OS_X2:
            OS2_0();
            OS1_0();
            OS0_1();
            break;

        case AD_OS_X4:
            OS2_0();
            OS1_1();
            OS0_0();
            break;

        case AD_OS_X8:
            OS2_0();
            OS1_1();
            OS0_1();
            break;

        case AD_OS_X16:
            OS2_1();
            OS1_0();
            OS0_0();
            break;

        case AD_OS_X32:
            OS2_1();
            OS1_0();
            OS0_1();
            break;

        case AD_OS_X64:
            OS2_1();
            OS1_1();
            OS0_0();
            break;

        case AD_OS_NO:
        default:
            g_tAD7606.ucOS = AD_OS_NO;
            OS2_0();
            OS1_0();
            OS0_0();
            break;
    }
}      

76.6.10   第10步,AD7606量程設定

AD7606支援兩種量程,±5V和±10V,實作代碼如下:

/*
*********************************************************************************************************
*    函 數 名: AD7606_SetInputRange
*    功能說明: 配置AD7606模拟信号輸入量程。
*    形    參: _ucRange : 0 表示正負5V   1表示正負10V
*    返 回 值: 無
*********************************************************************************************************
*/
void AD7606_SetInputRange(uint8_t _ucRange)
{
    if (_ucRange == 0)
    {
        g_tAD7606.ucRange = 0;
        RANGE_0();    /* 設定為正負5V */
    }
    else
    {
        g_tAD7606.ucRange = 1;
        RANGE_1();    /* 設定為正負10V */
    }
}      

76.6.11   第11步,操作資料位寬注意事項

在bsp_fmc_ad7606.c檔案開頭有個宏定義

#define

AD7606_RESULT() *(__IO uint16_t

*)0x60003000

特别注意,這裡是要操作位址0x60003000上的16位資料空間,即做了一個強制轉換uint16_t *。

76.7 AD7606闆級支援包(bsp_fmc_ad7606.c)

AD7606驅動檔案bsp_fmc_ad7606.c主要實作了如下幾個API供使用者調用:

  •   bsp_InitAD7606
  •   AD7606_SetOS
  •   AD7606_SetInputRange
  •   AD7606_Reset
  •   AD7606_StartConvst
  •   AD7606_ReadNowAdc
  •   AD7606_EnterAutoMode
  •   AD7606_StartRecord
  •   AD7606_StopRecord
  •   AD7606_FifoNewData
  •   AD7606_ReadFifo
  •   AD7606_FifoFull

76.7.1 函數bsp_InitAD7606

函數原型:

void

bsp_InitAD7606(void)

函數描述:

主要用于AD7606的初始化。

76.7.2 函數AD7606_SetOS

AD7606_SetOS(uint8_t _ucOS)

此函數用于配置AD7606數字濾波器,也就設定過采樣倍率。通過設定

AD7606_OS0、OS1、OS2口線的電平組合狀态決定過采樣倍率。啟動AD轉換之後,AD7606内部自動實作剩餘樣本的采集,然後求平均值輸出。

過采樣倍率越高,轉換時間越長。

無過采樣時,AD轉換時間 = 3.45us - 4.15us。

2倍過采樣時 = 7.87us - 9.1us。

4倍過采樣時 = 16.05us - 18.8us。

8倍過采樣時 = 33us - 39us。

16倍過采樣時 = 66us - 78us。

32倍過采樣時 = 133us - 158us。

64倍過采樣時 = 257us - 315us。

函數參數:

  •   第1個參數為範圍0 – 6,分别對應無過采樣,2倍過采樣,4倍過采樣,8倍過采樣,16倍過采樣,32倍過采樣和64倍過采樣。

76.7.3 函數AD7606_SetInputRange

AD7606_SetInputRange(uint8_t _ucRange)

配置AD7606模拟信号輸入量程。

  •   第1個參數為0 表示正負5V ,1表示正負10V。

76.7.4 函數AD7606_Reset

void AD7606_Reset(void)

此函數用于硬體複位AD7606,複位之後恢複到正常工作狀态。

76.7.5 函數AD7606_StartConvst

AD7606_StartConvst(void)

此函數用于啟動1次ADC轉換。

76.7.6 函數AD7606_ReadNowAdc

AD7606_ReadNowAdc(void)

此函數用于讀取8路采樣結果,結果存儲在全局變量 g_tAD7606。

76.7.7 函數AD7606_EnterAutoMode

AD7606_EnterAutoMode(uint32_t _ulFreq)

此函數用于配置硬體工作在自動采集模式,結果存儲在FIFO緩沖區。一般不單獨調用,函數AD7606_StartRecord會調用。

  •   第1個參數是采樣頻率,範圍1-200KHz,機關Hz。

76.7.8 函數AD7606_StartRecord

void AD7606_StartRecord(uint32_t

_ulFreq)

用于啟動采集。

76.7.9 函數AD7606_StopRecord

AD7606_StopRecord(void)

此函數用于停止采集定時器。函數AD7606_StartRecord和AD7606_StopRecord是配套的。

76.7.10   函數AD7606_FifoNewData

uint8_t AD7606_HasNewData(void)

此函數用于判斷FIFO中是否有新資料。

  •   傳回值,1 表示有,0表示暫無資料。

76.7.11   函數AD7606_ReadFifo

uint8_t

AD7606_ReadFifo(uint16_t *_usReadAdc)

此函數用于從FIFO中讀取一個ADC值。

  •   第1個參數是存放ADC結果的變量指針。
  •   傳回值,1 表示OK,0表示暫無資料。

76.7.12   函數AD7606_FifoFull

uint8_t AD7606_FifoFull(void)

此函數用于判斷FIFO是否滿。

  •   傳回值,1 表示滿,0表示未滿。

J-Scope專題教程(實時展示要用J-Scope的RTT模式),本章配套例子也做了支援:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=86881 。

看完專題教程,基本就會操作了,這裡有三點注意事項需要大家提前有個了解。另外,推薦使用MDK版工程做測試J-Scope,IAR版容易測試不正常。

76.8.1 J-Scope閃退問題解決辦法

如下界面,不要點選選擇按鈕,閃退就是因為點選了這個選擇按鈕。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

直接手動填寫型号即可,比如STM32H743XI,STM32F429BI,STM32F407IG,STM32F103ZE等。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.8.2 J-Scope多通道傳輸實作

J-Scope的多通道傳輸配置好函數SEGGER_RTT_ConfigUpBuffer即可,主要是通過第2個參數實作的。

/*
        配置通道1,上行配置
        預設情況下,J-Scope僅顯示1個通道。
        上傳1個通道的波形,配置第2個參數為JScope_i2
        上傳2個通道的波形,配置第2個參數為JScope_i2i2
        上傳3個通道的波形,配置第2個參數為JScope_i2i2i2
        上傳4個通道的波形,配置第2個參數為JScope_i2i2i2i2
        上傳5個通道的波形,配置第2個參數為JScope_i2i2i2i2i2
        上傳6個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2
        上傳7個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2i2
        上傳8個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2i2i2
    */    
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_i2", buf, 20480, SEGGER_RTT_MODE_NO_BLOCK_SKIP);      

使用函數SEGGER_RTT_Write上傳資料時,要跟配置的通道數比對,比如配置的三個通道,就需要調用三次函數:

SEGGER_RTT_Write(1, &(g_tAD7606.sNowAdc[0]), 2);
SEGGER_RTT_Write(1, &(g_tAD7606.sNowAdc[1]), 2);    
SEGGER_RTT_Write(1, &(g_tAD7606.sNowAdc[2]), 2);      

多路效果:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

76.8.3 J-Scope帶寬問題

普通的JLINK時脈速度8 - 12MHz時,

J-Scope的速度基本可以達到500KB/S(注意,機關是位元組)AD7606的最高采樣率是200Ksps,16bit,那麼一路采集就有400KB/S的速速,是以要根據設定的采樣率設定要顯示的J-Scope通道數,如果超出了最高通信速度,波形顯示會混亂。

       200Ksps時,實時顯示1路

       100Ksps時,實時顯示2路

       50Ksps時, 實時顯示4路

       25Ksps時, 實時顯示8路

實際速度以底欄的展示為準,如果與設定的速度差異較大,說明傳輸異常了。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

AD7606移植步驟如下:

  • 第1步:複制bsp_fmc_ad7606.c和bsp_fmc_ad7606.h到自己的工程目錄,并添加到工程裡面。
  • 第2步:根據使用的CONVST引腳,BUSY引腳,過采樣引腳,量程控制引腳,複位引腳,修改bsp_fmc_ad7606.c開頭的宏定義。

這裡要特别注意過采樣引腳,量程控制引腳和複位引腳是采用的擴充IO,需要大家根據自己的情況修改。

/* CONVST 啟動ADC轉換的GPIO = PC6 */
#define CONVST_RCC_GPIO_CLK_ENABLE    __HAL_RCC_GPIOC_CLK_ENABLE
#define CONVST_TIM8_CLK_DISABLE     __HAL_RCC_TIM8_CLK_DISABLE
#define CONVST_GPIO        GPIOC
#define CONVST_PIN        GPIO_PIN_6
#define CONVST_TIMX        TIM8
#define CONVST_TIMCH    1

/* BUSY 轉換完畢信号 = PE5 */
#define BUSY_RCC_GPIO_CLK_ENABLE __HAL_RCC_GPIOE_CLK_ENABLE
#define BUSY_GPIO        GPIOE
#define BUSY_PIN        GPIO_PIN_5
#define BUSY_IRQn        EXTI9_5_IRQn
#define BUSY_IRQHandler    EXTI9_5_IRQHandler

/* 設定過采樣的IO, 在擴充的74HC574上 */
#define OS0_1()        HC574_SetPin(AD7606_OS0, 1)
#define OS0_0()        HC574_SetPin(AD7606_OS0, 0)
#define OS1_1()        HC574_SetPin(AD7606_OS1, 1)
#define OS1_0()        HC574_SetPin(AD7606_OS1, 0)
#define OS2_1()        HC574_SetPin(AD7606_OS2, 1)
#define OS2_0()        HC574_SetPin(AD7606_OS2, 0)

/* 啟動AD轉換的GPIO : PC6 */
#define CONVST_1()        CONVST_GPIO->BSRR = CONVST_PIN
#define CONVST_0()        CONVST_GPIO->BSRR = ((uint32_t)CONVST_PIN << 16U)

/* 設定輸入量程的GPIO, 在擴充的74HC574上 */
#define RANGE_1()    HC574_SetPin(AD7606_RANGE, 1)
#define RANGE_0()    HC574_SetPin(AD7606_RANGE, 0)

/* AD7606複位口線, 在擴充的74HC574上 */
#define RESET_1()    HC574_SetPin(AD7606_RESET, 1)
#define RESET_0()    HC574_SetPin(AD7606_RESET, 0)      
  • 第3步:根據具體用到的FMC引腳,修改函數AD7606_CtrlLinesConfig裡面做的IO配置。
  • 第4步:根據使用的FMC BANK,修改函數AD7606_FSMCConfig裡面的BANK配置,這點非常容易疏忽。
  • 第5步:注意MPU配置,詳情見本章77.7.5小節。
  • 第6步:初始化AD7606。

bsp_InitAD7606();  /* 配置AD7606所用的GPIO */

  • 第7步:AD7606驅動主要用到HAL庫的FMC驅動檔案,簡單省事些可以添加所有HAL庫C源檔案進來。
  • 第8步:應用方法看本章節配套例子即可。

76.10         

實驗例程設計架構

通過程式設計架構,讓大家先對配套例程有一個全面的認識,然後再了解細節,本次實驗例程的設計架構如下:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

  第1階段,上電啟動階段:

  • 這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  • 第1部分,硬體初始化,主要是MPU,Cache,HAL庫,系統時鐘,滴答定時器和LED。
  • 第2部分,應用程式設計部分,測試AD7606的兩種采集方案。

76.11         

實驗例程說明(MDK)

配套例子:

V7-056_AD7606的FMC總線驅動方式實作(8通道同步采樣, 16bit, 正負10V)

實驗目的:

  1. 學習AD7606的FMC驅動方式實作。
  1. 闆子上電後,預設是軟體定時采集,0.5秒一次,适合序列槽展示資料。
  2. 如果需要使用J-Scope實時展示采集的波形效果,需要按下K2按鍵切換到FIFO模式。
  3. 如果使用的JLINK速度不夠快,導緻J-Scope無法最高速度實時上傳,可以使用搖杆上下鍵設定過采樣來降低上傳速度。
  4. 預設情況下,程式僅上傳了AD7606通道1采集的資料。
  5. 序列槽資料展示推薦使用SecureCRT,因為資料展示做了特别處理,友善采集資料在序列槽軟體同一個位置不斷重新整理。

實驗内容:

1、AD7606的FMC驅動做了兩種采集方式

2、資料展示方式:

(1)軟體查詢方式,資料通過序列槽列印輸出。

(2)FIFO工作模式,資料通過J-Scope實時輸出。

(3)J-Scope的實時輸出方法請看V7闆子使用者手冊對應的AD7606章節。

3、将模拟輸入接地時,采樣值是0左右。

4、模拟輸入端懸空時,采樣值在某個範圍浮動(這是正常的,這是AD7606内部輸入電阻導緻的浮動電壓)。

5、出廠的AD7606子產品預設是8080 并行接口。如果用SPI接口模式,需要修改 R1 R2電阻配置。

6、配置CVA CVB 引腳為PWM輸出模式,周期設定為需要的采樣頻率,之後MCU将産生周期非常穩定的AD轉換信号。

實驗操作:

  1. 啟動一個自動重裝軟體定時器,每100ms翻轉一次LED2。
  2. K1鍵       : 切換量程(5V或10V)。
  3. K2鍵       : 進入FIFO工作模式。
  4. K3鍵       : 進入軟體定時采集模式。
  5. 搖杆上下鍵 : 調節過采樣參數。

上電後序列槽列印的資訊:

波特率 115200,資料位 8,奇偶校驗位無,停止位 1。

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

J-Scope波形效果:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

子產品插入位置:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

程式設計:

  系統棧大小配置設定:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

  RAM空間用的DTCM:

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)

  硬體外設初始化

硬體外設的初始化是在 bsp.c 檔案實作:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:
       - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設定NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。
       - 預設不開啟,如果要使能此選項,務必看V7開發闆使用者手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
bsp_InitDWT();      /* 初始化DWT時鐘周期計數器 */       
    bsp_InitKey();         /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();       /* 初始化滴答定時器 */
    bsp_InitLPUart();     /* 初始化序列槽 */
    bsp_InitExtIO();     /* 初始化FMC總線74HC574擴充IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();         /* 初始化LED */    
bsp_InitExtSDRAM(); /* 初始化SDRAM */

    /* 針對不同的應用程式,添加需要的底層驅動子產品初始化函數 */    
    bsp_InitAD7606();    /* 配置AD7606所用的GPIO */
}      

  MPU配置和Cache配置:

資料Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴充IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴充IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}      

  每10ms調用一次按鍵處理:

按鍵處理是在滴答定時器中斷裡面實作,每10ms執行一次檢測。

/*
*********************************************************************************************************
*    函 數 名: bsp_RunPer10ms
*    功能說明: 該函數每隔10ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程式。一些處理時間要求
*              不嚴格的任務可以放在此函數。比如:按鍵掃描、蜂鳴器鳴叫控制等。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}      

  主功能:

主程式實作如下操作:

  •   啟動一個自動重裝軟體定時器,每100ms翻轉一次LED2。
  •   K1鍵       : 切換量程(5V或10V)。
  •   K2鍵       : 進入FIFO工作模式。
  •   K3鍵       : 進入軟體定時采集模式。
  •   搖杆上下鍵 : 調節過采樣參數。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程式入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    bsp_Init();        /* 硬體初始化 */
    
    PrintfLogo();    /* 列印例程名稱和版本等資訊 */

    DemoFmcAD7606(); /* AD7606測試 */
}

/*
*********************************************************************************************************
*    函 數 名: DemoFmcAD7606
*    功能說明: AD7606測試
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoFmcAD7606(void)
{
    uint8_t ucKeyCode;
    uint8_t ucRefresh = 0;
    uint8_t ucFifoMode;
    
    sfDispMenu();        /* 列印指令提示 */

    ucFifoMode = 0;         /* AD7606進入普通工作模式 */
    ucRefresh = 0;        /* 資料在序列槽重新整理的标志 */
    
    AD7606_SetOS(AD_OS_NO);        /* 無過采樣 */
    AD7606_SetInputRange(1);    /* 0表示輸入量程為正負5V, 1表示正負10V */
    AD7606_StartConvst();        /* 啟動1次轉換 */

    bsp_StartAutoTimer(0, 500);    /* 啟動1個500ms的自動重裝的定時器 */
    bsp_StartAutoTimer(3, 200);    /* 啟動1個200ms的自動重裝的定時器 */
    
    /*
        配置通道1,上行配置
        預設情況下,J-Scope僅顯示1個通道。
        上傳1個通道的波形,配置第2個參數為JScope_i2
        上傳2個通道的波形,配置第2個參數為JScope_i2i2
        上傳3個通道的波形,配置第2個參數為JScope_i2i2i2
        上傳4個通道的波形,配置第2個參數為JScope_i2i2i2i2
        上傳5個通道的波形,配置第2個參數為JScope_i2i2i2i2i2
        上傳6個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2
        上傳7個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2i2
        上傳8個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2i2i2
    */    
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_i2", buf, 20480, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c檔案。使用者可以修改這個函數實作CPU休眠和喂狗 */
        
        /* 判斷定時器逾時時間 */
        if (bsp_CheckTimer(3))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        if (ucRefresh == 1)
        {
            ucRefresh = 0;

            /* 處理資料 */
            AD7606_Mak();
                                         
            /* 列印ADC采樣結果 */
            AD7606_Disp();        
        }

        if (ucFifoMode == 0)    /* AD7606 普通工作模式 */
        {
            if (bsp_CheckTimer(0))
            {
                /* 每隔500ms 進來一次. 由軟體啟動轉換 */
                AD7606_ReadNowAdc();        /* 讀取采樣結果 */
                AD7606_StartConvst();        /* 啟動下次轉換 */

                ucRefresh = 1;    /* 重新整理顯示 */
            }
        }
        else
        {
            /*
                在FIFO工作模式,bsp_AD7606自動進行采集,資料存儲在FIFO緩沖區。
                結果可以通過下面的函數讀取:
                uint8_t AD7606_ReadFifo(uint16_t *_usReadAdc)

                大家可以将資料儲存到SD卡,或者儲存到外部SRAM。

                本例未對FIFO中的資料進行處理,進行列印目前最新的樣本值和J-Scope的實時輸出展示。

                如果主程式不能及時讀取FIFO資料,那麼 AD7606_FifoFull() 将傳回真。

                8通道200K采樣時,資料傳輸率 = 200 000 * 2 * 8 = 3.2MB/S
            */

            if (bsp_CheckTimer(0))
            {
                ucRefresh = 1;    /* 重新整理顯示 */
            }
        }

        /* 按鍵檢測由背景systick中斷服務程式實作,我們隻需要調用bsp_GetKey讀取鍵值即可。這個函數不會
        等待按鍵按下,這樣我們可以在while循環内做其他的事情 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時傳回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下 切換量程 */
                    if (g_tAD7606.ucRange == 0)
                    {
                        AD7606_SetInputRange(1);
                    }
                    else
                    {
                        AD7606_SetInputRange(0);
                    }
                    ucRefresh = 1;
                    break;

                case KEY_DOWN_K2:                        /* K2鍵按下 */
                    ucFifoMode = 1;                      /* AD7606進入FIFO工作模式 */
                    g_tAD7606.ucOS = 1;                    /* 無過采樣 */
                    AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    /* 啟動100kHz采樣速率 */
                    AD7606_SetOS(g_tAD7606.ucOS);       /* 設定無過采樣 */
                    printf("\33[%dA", (int)1);          /* 光标上移n行 */    
                    printf("AD7606進入FIFO工作模式 (200KHz 8通道同步采集)...\r\n");
                    break;

                case KEY_DOWN_K3:            /* K3鍵按下 */
                    AD7606_StopRecord();    /* 停止記錄 */
                    ucFifoMode = 0;         /* AD7606進入普通工作模式 */
                    g_tAD7606.ucOS = 0;     /* 無過采樣 */
                    AD7606_SetOS(g_tAD7606.ucOS);
                    printf("\33[%dA", (int)1);  /* 光标上移n行 */
                    printf("AD7606進入普通工作模式(0.5s定時8通道同步采集)...\r\n");
                    break;

                case JOY_DOWN_U:            /* 搖杆UP鍵按下 */
                    if (g_tAD7606.ucOS < 6)
                    {
                        g_tAD7606.ucOS++;
                    }
                    
                    AD7606_SetOS(g_tAD7606.ucOS);

                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
                            /* 啟動目前過采樣下最高速度 */
                        AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    
                    }
                    
                    ucRefresh = 1;
                    break;

                case JOY_DOWN_D:            /* 搖杆DOWN鍵按下 */
                    if (g_tAD7606.ucOS > 0)
                    {
                        g_tAD7606.ucOS--;
                    }
                    AD7606_SetOS(g_tAD7606.ucOS);
                    ucRefresh = 1;
                    
                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
/* 啟動目前過采樣下最高速度 */
                        AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    
                    }
                    break;

                default:
                    /* 其他的鍵值不處理 */
                    break;
            }
        }
    }
}      

76.12         

實驗例程說明(IAR)

【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
【STM32H7教程】第76章 STM32H7的FMC總線應用之驅動AD7606(8通道同步采樣, 16bit, 正負10V)
/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:
       - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設定NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。
       - 預設不開啟,如果要使能此選項,務必看V7開發闆使用者手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
bsp_InitDWT();      /* 初始化DWT時鐘周期計數器 */       
    bsp_InitKey();         /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();       /* 初始化滴答定時器 */
    bsp_InitLPUart();     /* 初始化序列槽 */
    bsp_InitExtIO();     /* 初始化FMC總線74HC574擴充IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();         /* 初始化LED */    
bsp_InitExtSDRAM(); /* 初始化SDRAM */

    /* 針對不同的應用程式,添加需要的底層驅動子產品初始化函數 */    
    bsp_InitAD7606();    /* 配置AD7606所用的GPIO */
}      
/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴充IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}      
/*
*********************************************************************************************************
*    函 數 名: bsp_RunPer10ms
*    功能說明: 該函數每隔10ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程式。一些處理時間要求
*              不嚴格的任務可以放在此函數。比如:按鍵掃描、蜂鳴器鳴叫控制等。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}      
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程式入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    bsp_Init();        /* 硬體初始化 */
    
    PrintfLogo();    /* 列印例程名稱和版本等資訊 */

    DemoFmcAD7606(); /* AD7606測試 */
}

/*
*********************************************************************************************************
*    函 數 名: DemoFmcAD7606
*    功能說明: AD7606測試
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoFmcAD7606(void)
{
    uint8_t ucKeyCode;
    uint8_t ucRefresh = 0;
    uint8_t ucFifoMode;
    
    sfDispMenu();        /* 列印指令提示 */

    ucFifoMode = 0;         /* AD7606進入普通工作模式 */
    ucRefresh = 0;        /* 資料在序列槽重新整理的标志 */
    
    AD7606_SetOS(AD_OS_NO);        /* 無過采樣 */
    AD7606_SetInputRange(1);    /* 0表示輸入量程為正負5V, 1表示正負10V */
    AD7606_StartConvst();        /* 啟動1次轉換 */

    bsp_StartAutoTimer(0, 500);    /* 啟動1個500ms的自動重裝的定時器 */
    bsp_StartAutoTimer(3, 200);    /* 啟動1個200ms的自動重裝的定時器 */
    
    /*
        配置通道1,上行配置
        預設情況下,J-Scope僅顯示1個通道。
        上傳1個通道的波形,配置第2個參數為JScope_i2
        上傳2個通道的波形,配置第2個參數為JScope_i2i2
        上傳3個通道的波形,配置第2個參數為JScope_i2i2i2
        上傳4個通道的波形,配置第2個參數為JScope_i2i2i2i2
        上傳5個通道的波形,配置第2個參數為JScope_i2i2i2i2i2
        上傳6個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2
        上傳7個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2i2
        上傳8個通道的波形,配置第2個參數為JScope_i2i2i2i2i2i2i2i2
    */    
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_i2", buf, 20480, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c檔案。使用者可以修改這個函數實作CPU休眠和喂狗 */
        
        /* 判斷定時器逾時時間 */
        if (bsp_CheckTimer(3))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        if (ucRefresh == 1)
        {
            ucRefresh = 0;

            /* 處理資料 */
            AD7606_Mak();
                                         
            /* 列印ADC采樣結果 */
            AD7606_Disp();        
        }

        if (ucFifoMode == 0)    /* AD7606 普通工作模式 */
        {
            if (bsp_CheckTimer(0))
            {
                /* 每隔500ms 進來一次. 由軟體啟動轉換 */
                AD7606_ReadNowAdc();        /* 讀取采樣結果 */
                AD7606_StartConvst();        /* 啟動下次轉換 */

                ucRefresh = 1;    /* 重新整理顯示 */
            }
        }
        else
        {
            /*
                在FIFO工作模式,bsp_AD7606自動進行采集,資料存儲在FIFO緩沖區。
                結果可以通過下面的函數讀取:
                uint8_t AD7606_ReadFifo(uint16_t *_usReadAdc)

                大家可以将資料儲存到SD卡,或者儲存到外部SRAM。

                本例未對FIFO中的資料進行處理,進行列印目前最新的樣本值和J-Scope的實時輸出展示。

                如果主程式不能及時讀取FIFO資料,那麼 AD7606_FifoFull() 将傳回真。

                8通道200K采樣時,資料傳輸率 = 200 000 * 2 * 8 = 3.2MB/S
            */

            if (bsp_CheckTimer(0))
            {
                ucRefresh = 1;    /* 重新整理顯示 */
            }
        }

        /* 按鍵檢測由背景systick中斷服務程式實作,我們隻需要調用bsp_GetKey讀取鍵值即可。這個函數不會
        等待按鍵按下,這樣我們可以在while循環内做其他的事情 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時傳回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下 切換量程 */
                    if (g_tAD7606.ucRange == 0)
                    {
                        AD7606_SetInputRange(1);
                    }
                    else
                    {
                        AD7606_SetInputRange(0);
                    }
                    ucRefresh = 1;
                    break;

                case KEY_DOWN_K2:                        /* K2鍵按下 */
                    ucFifoMode = 1;                      /* AD7606進入FIFO工作模式 */
                    g_tAD7606.ucOS = 1;                    /* 無過采樣 */
                    AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    /* 啟動100kHz采樣速率 */
                    AD7606_SetOS(g_tAD7606.ucOS);       /* 設定無過采樣 */
                    printf("\33[%dA", (int)1);          /* 光标上移n行 */    
                    printf("AD7606進入FIFO工作模式 (200KHz 8通道同步采集)...\r\n");
                    break;

                case KEY_DOWN_K3:            /* K3鍵按下 */
                    AD7606_StopRecord();    /* 停止記錄 */
                    ucFifoMode = 0;         /* AD7606進入普通工作模式 */
                    g_tAD7606.ucOS = 0;     /* 無過采樣 */
                    AD7606_SetOS(g_tAD7606.ucOS);
                    printf("\33[%dA", (int)1);  /* 光标上移n行 */
                    printf("AD7606進入普通工作模式(0.5s定時8通道同步采集)...\r\n");
                    break;

                case JOY_DOWN_U:            /* 搖杆UP鍵按下 */
                    if (g_tAD7606.ucOS < 6)
                    {
                        g_tAD7606.ucOS++;
                    }
                    
                    AD7606_SetOS(g_tAD7606.ucOS);

                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
                            /* 啟動目前過采樣下最高速度 */
                        AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    
                    }
                    
                    ucRefresh = 1;
                    break;

                case JOY_DOWN_D:            /* 搖杆DOWN鍵按下 */
                    if (g_tAD7606.ucOS > 0)
                    {
                        g_tAD7606.ucOS--;
                    }
                    AD7606_SetOS(g_tAD7606.ucOS);
                    ucRefresh = 1;
                    
                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
/* 啟動目前過采樣下最高速度 */
                        AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    
                    }
                    break;

                default:
                    /* 其他的鍵值不處理 */
                    break;
            }
        }
    }      

76.13   總結

本章節涉及到的知識點非常多,實戰性較強,需要大家稍花點精力去研究。

微信公衆号:armfly_com

安富萊論壇:www.armbbs.cn

安富萊淘寶:https://armfly.taobao.com