【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 總結

 

 

76.1 初學者重要提示

  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采集的數據。

76.2 ADC結構分類

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

 

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相比,可以實現更高的分辨率或更小的裸片尺寸。

76.3 AD7606硬件設計

這里將開發板上的AD7606硬件接口,普通型AD7606模塊,屏蔽型AD7606模塊和磁耦高速隔離型AD7606模塊為大家做個說明。

AD7606的原理圖下載:

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

76.3.1 AD7606硬件接口

V7板子上AD7606模塊的插座的原理圖如下:

 

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

 

為了方便大家更好的理解接線,下面是框圖:

 

模塊引腳說明:

  •   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接口方式,接線框圖如下:

 

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。

產品效果:

 

 

 

8080或者SPI接口方式選擇

出廠的AD7606模塊缺省是8080並行接口,如果用SPI接口模式,需要修改R1、R2電阻配置。

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

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

 

76.3.3 AD7606模塊(屏蔽版)

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

 

 

 

 

 

 

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間距排針,節約主板面積。

產品效果:

 

 

引腳定義和接線圖:

 

 

 

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

驅動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。

 

 

76.4.2 AD7606常用引腳的作用

AD7606的封裝形式:

 

 

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

  •   AVcc

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

  •   AGND

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

  •   OS2 OS1 OS2 :

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

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

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

  •   CONVSTA,CONVSTB :

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

  •   RAGE :

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

  •  RD /SCL:

讀信號,低電平有效。

  •   RESET

復位信號。

  •   BUSY :

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

  •   CS :

片選信號,低電平有效。

  •   FRST :

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

  •   VDriver:

通信接口電平。

  •   DB0-DB15 :

數據總線。

  •   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的計算公式如下:

 

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

  •   VIN

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

  •   REF

一般使用內部基准,即2.5V。

76.4.4 AD7606時序圖

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

1、AD7606的CONVST轉換時序(轉換之后讀取數據):

 

  •   t5

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

  •   tCYCLE

並行模式,轉換后並讀取數據的最大值是5us,即最高支持的時鍾速度是20MHz及其以上。

  •   tCONV

轉換時間。

 

  •   t3

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

  •   t4

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

 

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

 

  •   t8

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

  •   t10

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

 

  •   t11

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

  •   t9

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

  •   13到t17 

這幾個參數了解下即可:

 

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

 

這個時序里面最重要的是t12

  •   t12

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

 

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

76.4.5 AD7606的過采樣

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

 

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

注意正確的理解過采樣,比如我們設置是1Ksps采樣率,64倍過采樣。意思是指每次采樣,AD7606會采樣64次數據並求平均,相當於AD7606以64Ksps進行采樣的,只是將每64個采樣點的值做了平均,用戶得到的值就是平均后的數值。因此,如果使用AD7606最高的200Ksps采樣率,就不可以使用過采樣了。

76.5 AD7606的FMC接口硬件設計

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

76.5.1 FMC的塊區分配

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

 

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

76.5.2 譯碼器及其地址計算

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

 

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

 

 

通過上面的原理圖和真值表就比較好理解了,真值表的輸出是由片選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小節有詳細講解。

 

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位模式,大家應該也都會計算了。

76.6 AD7606的FMC接口驅動設計

AD7606的程序驅動框架設計如下:

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

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:

 

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

 

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

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

 

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

  •   DATAST(DataSetupTime,數據建立時間)

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

 

  •   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通過譯碼器所管理的所有設備地址全部設置為此配置:

 

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方式有個整體認識:

  •   啟動采集函數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

函數原型:

void 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

函數原型:

void 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

函數原型:

void AD7606_StartConvst(void)

函數描述:

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

76.7.6 函數AD7606_ReadNowAdc

函數原型:

void AD7606_ReadNowAdc(void)

函數描述:

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

76.7.7 函數AD7606_EnterAutoMode

函數原型:

void AD7606_EnterAutoMode(uint32_t _ulFreq)

函數描述:

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

函數參數:

  •   第1個參數是采樣頻率,范圍1-200KHz,單位Hz。

76.7.8 函數AD7606_StartRecord

函數原型:

void AD7606_StartRecord(uint32_t _ulFreq)

函數描述:

用於啟動采集。

函數參數:

  •   第1個參數是采樣頻率,范圍1-200KHz,單位Hz。

76.7.9 函數AD7606_StopRecord

函數原型:

void 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表示未滿。

76.8 J-Scope實時展示AD7606采集數據說明

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

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

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

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

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

 

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

 

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);

 

多路效果:

 

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路

 

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

 

76.9 AD7606驅動移植和使用

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          實驗例程設計框架

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

  第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驅動做了兩種采集方式

(1)軟件定時獲取方式,適合低速查詢獲取。

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

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。

 

J-Scope波形效果:

 

模塊插入位置:

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 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)

配套例子:

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驅動做了兩種采集方式

(1)軟件定時獲取方式,適合低速查詢獲取。

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

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。

 

J-Scope波形效果:

 

模塊插入位置:

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 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.13   總結

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM