最新教程下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255
第34章 STM32F429的SPI總線應用之驅動DAC8501(雙路輸出,16bit分辨率,0-5V)
本章節為大家講解標准SPI接線方式驅動模數轉換器DAC8501。
34.1 初學者重要提示
34.2 DAC結構分類和技術術語
34.3 DAC8501硬件設計
34.4 DAC8501關鍵知識點整理(重要)
34.5 DAC8501驅動設計
34.6 SPI總線板級支持包(bsp_spi_bus.c)
34.7 DAC8501支持包中斷方式(bsp_spi_dac8501.c)
34.8 DAC8501驅動移植和使用
34.9 實驗例程設計框架
34.10 實驗例程說明(MDK)
34.11 實驗例程說明(IAR)
34.12 總結
34.1 初學者重要提示
- 學習本章節前,務必優先學習第31章。
- DAC8501模塊上帶了兩片8501,每片是單通道DAC,帶片上輸出緩沖運放,軌到軌輸出,16bit分辨率,支持30MHz的SPI時鍾速度。
- 我們的H7板子配套了SPI + DMA方式控制DAC8501,而F4系列不方便實現,確切的說是可以用DMA方式,但是不方便控制寫入速度,需要借助定時器中斷進行更新,實用價值不是很大。
- DAC8501數據手冊,模塊原理圖和接線圖都已經放到本章教程配置例子的Doc文件里。
- 文件bsp_spi_bus.c文件公共的總線驅動文件,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI設備的配置。
34.2 DAC結構分類和技術術語
在本教程的第33章進行了詳細說明。
34.3 DAC8501硬件設計
DAC的原理圖下載:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=97262 。
34.3.1 DAC8501模塊規格
- 產品規格:
1、供電電壓: 2.7 - 5.5V【3.3V供電時,輸出電壓也可以到5V】。
2、通道數: 2路 (通過2片DAC8501E實現)。
3、輸出電壓范圍 : 0 - 5V【零位 < 0.020V, 滿位 > 4.970V】。
4、分辨率: 16位。
5、功耗 : 小於10mA。
6、MCU接口 :高速 SPI (30M) 支持 3.3V和5V單片機。
7、DAC輸出模擬帶寬:350KHz。
8、DAC輸出響應: 10uS 到 0.003% FSR。
- 產品特點:
1、輸出和供電電壓無關;模塊內帶升壓電路和5V基准。
2、自適應單片機的電平(2.7 - 5V 均可以)。
3、輸出電壓軌到軌,最高電壓可以到 4.970V 以上。
- 產品效果:
34.3.2 DAC8501硬件接口
V6板子上DAC8501模塊的插座的原理圖如下(NRF24L01,AD9833,DAC8563和TM7705都是用的而這個插座):
實際對應開發的位置如下:
34.4 DAC8501關鍵知識點整理(重要)
驅動DAC8501需要對下面這些知識點有個認識。
34.4.1 DAC8501基礎信息
- 單通道DAC,帶片上輸出緩沖運放,軌到軌輸出,16bit分辨率,支持30MHz的SPI時鍾速度。
- 模擬輸出帶寬350KHz。
- 供電范圍2.7V到5.5V。
- 具有低功耗特性。
- 上電復位輸出0V。
34.4.2 DAC8501每個引腳的作用
DAC8501的封裝形式:
- Vdd
供電范圍2.7-5.5V。
- Vref
穩壓基准輸入。
- Vfb
輸出運放的反饋。
- Vout
模擬輸出電壓,輸出運放具有軌到軌特性。
- SYNC (片選)
低電平有效,當SYNC變為低電平時,它使能輸入移位寄存器,並且數據采樣在隨后的時鍾下降沿。 DAC輸出在第24個時鍾下降沿之后更新。 如果SYNC在第23個時鍾沿之前變高,SYNC的上升沿將充當中斷,並且DAC8501將忽略寫序列。
- SCLK
時鍾輸入端,支持30MHz。
- Din
串行時鍾輸入,每個時鍾下降沿將數據寫到的24bit的輸入移位寄存器。
- GND
接地端。
34.4.3 DAC8501輸出電壓計算公式
DAC8501的計算公式如下:
- D
配置DAC8501數據輸出寄存器的數值,范圍0 到2^16 – 1,即0到65535。
- VREF
使用外部參考電壓,由VREFIN引腳的輸入決定。
- Vout
輸出電壓。
34.4.4 DAC8501時序圖
DAC8501的時序圖如下:
這個時序里面有三個參數尤其重要,后面時序配置要用到(對於F4系列主要是第1個參數,H7系列這三個都要用的)。
- f(1)
供電2.7到3.6V時,最高時鍾20MHz。
供電3.6到5.5V時,最高時鍾30MHz。
- t(4)
SYNC低電平有效到SCLK第1個上降沿信號的時間沒有最小值限制,可以為0。
- t(8)
每傳輸24bit數據后,SYNC要保持一段時間的高電平。
供電2.7到3.6V時,最小要求50ns。
供電3.6到5.5V時,最小要求33ns。
34.4.5 DAC8501寄存器配置
DAC8501的寄存器配置是24bit格式:
控制DAC8501每次要傳輸24bit數據,高8bit控制位 + 16bit數據位。
控制位的PD1和PD0定義如下:
PD1 PD0 決定4種工作模式:
0 0 ---> 正常工作模式
0 1 ---> 輸出接1K歐到GND
1 0 ---> 輸出100K歐到GND
1 1 ---> 輸出高阻
34.5 DAC8501驅動設計(中斷更新方式)
DAC8501的程序驅動框架設計如下:
有了這個框圖,程序設計就比較好理解了。
34.5.1 第1步:SPI總線配置
spi總線配置通過如下兩個函數實現:
/* ********************************************************************************************************* * 函 數 名: bsp_InitSPIBus * 功能說明: 配置SPI總線。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_InitSPIBus(void) { g_spi_busy = 0; bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW); } /* ********************************************************************************************************* * 函 數 名: bsp_InitSPIParam * 功能說明: 配置SPI總線參數,時鍾分頻,時鍾相位和時鍾極性。 * 形 參: _BaudRatePrescaler SPI總線時鍾分頻設置,支持的參數如下: * SPI_BAUDRATEPRESCALER_2 2分頻 * SPI_BAUDRATEPRESCALER_4 4分頻 * SPI_BAUDRATEPRESCALER_8 8分頻 * SPI_BAUDRATEPRESCALER_16 16分頻 * SPI_BAUDRATEPRESCALER_32 32分頻 * SPI_BAUDRATEPRESCALER_64 64分頻 * SPI_BAUDRATEPRESCALER_128 128分頻 * SPI_BAUDRATEPRESCALER_256 256分頻 * * _CLKPhase 時鍾相位,支持的參數如下: * SPI_PHASE_1EDGE SCK引腳的第1個邊沿捕獲傳輸的第1個數據 * SPI_PHASE_2EDGE SCK引腳的第2個邊沿捕獲傳輸的第1個數據 * * _CLKPolarity 時鍾極性,支持的參數如下: * SPI_POLARITY_LOW SCK引腳在空閑狀態處於低電平 * SPI_POLARITY_HIGH SCK引腳在空閑狀態處於高電平 * * 返 回 值: 無 ********************************************************************************************************* */ void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity) { /* 提高執行效率,只有在SPI硬件參數發生變化時,才執行HAL_Init */ if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity) { return; } s_BaudRatePrescaler = _BaudRatePrescaler; s_CLKPhase = _CLKPhase; s_CLKPolarity = _CLKPolarity; /* 設置SPI參數 */ hspi.Instance = SPIx; /* 例化SPI */ hspi.Init.BaudRatePrescaler = _BaudRatePrescaler; /* 設置波特率 */ hspi.Init.Direction = SPI_DIRECTION_2LINES; /* 全雙工 */ hspi.Init.CLKPhase = _CLKPhase; /* 配置時鍾相位 */ hspi.Init.CLKPolarity = _CLKPolarity; /* 配置時鍾極性 */ hspi.Init.DataSize = SPI_DATASIZE_8BIT; /* 設置數據寬度 */ hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; /* 數據傳輸先傳高位 */ hspi.Init.TIMode = SPI_TIMODE_DISABLE; /* 禁止TI模式 */ hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */ hspi.Init.CRCPolynomial = 7; /* 禁止CRC后,此位無效 */ hspi.Init.NSS = SPI_NSS_SOFT; /* 使用軟件方式管理片選引腳 */ hspi.Init.Mode = SPI_MODE_MASTER; /* SPI工作在主控模式 */ /* 復位SPI */ if(HAL_SPI_DeInit(&hspi) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } if (HAL_SPI_Init(&hspi) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } }
關於這兩個函數有以下兩點要做個說明:
- 函數bsp_InitSPIBus里面的配置是個初始設置。實際驅動芯片時,會通過函數bsp_InitSPIParam做再配置。
- 函數bsp_InitSPIParam提供了時鍾分頻,時鍾相位和時鍾極性配置。驅動不同外設芯片時,基本上調整這三個參數就夠。當SPI接口上接了多個不同類型的芯片時,通過此函數可以方便的切換配置。
34.5.2 第2步:SPI總線的查詢,中斷和DMA方式設置
注:推薦使用查詢方式。
SPI驅動的查詢,中斷和DMA方式主要通過函數bsp_spiTransfer實現數據傳輸:
/* ********************************************************************************************************* * 選擇DMA,中斷或者查詢方式 ********************************************************************************************************* */ //#define USE_SPI_DMA /* DMA方式 */ //#define USE_SPI_INT /* 中斷方式 */ #define USE_SPI_POLL /* 查詢方式 */ uint8_t g_spiTxBuf[SPI_BUFFER_SIZE]; uint8_t g_spiRxBuf[SPI_BUFFER_SIZE]; /* ********************************************************************************************************* * 函 數 名: bsp_spiTransfer * 功能說明: 啟動數據傳輸 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_spiTransfer(void) { if (g_spiLen > SPI_BUFFER_SIZE) { return; } /* DMA方式傳輸 */ #ifdef USE_SPI_DMA wTransferState = TRANSFER_WAIT; if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } while (wTransferState == TRANSFER_WAIT) { ; } #endif /* 中斷方式傳輸 */ #ifdef USE_SPI_INT wTransferState = TRANSFER_WAIT; if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } while (wTransferState == TRANSFER_WAIT) { ; } #endif /* 查詢方式傳輸 */ #ifdef USE_SPI_POLL if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } #endif }
通過開頭宏定義可以方便的切換中斷,查詢和DMA方式。
34.5.3 第3步:DAC8501的時鍾極性和時鍾相位配置
首先回憶下STM32F4支持的4種時序配置。
- 當CPOL = 1, CPHA = 1時
SCK引腳在空閑狀態處於高電平,SCK引腳的第2個邊沿捕獲傳輸的第1個數據。
- 當CPOL = 0, CPHA = 1時
SCK引腳在空閑狀態處於低電平,SCK引腳的第2個邊沿捕獲傳輸的第1個數據。
- 當CPOL = 1, CPHA = 0時
SCK引腳在空閑狀態處於高電平,SCK引腳的第1個邊沿捕獲傳輸的第1個數據。
- 當CPOL = 0 ,CPHA= 0時
SCK引腳在空閑狀態處於低電平,SCK引腳的第1個邊沿捕獲傳輸的第1個數據。
有了F4支持的時序配置,再來看下DAC8501的時序圖:
首先DAC8501是下降升沿做數據采集,所以STM32F4的可選的配置就是:
CHOL = 0, CPHA = 1
CHOL = 1, CPHA = 0
對於這兩種情況的主要區別是空閑狀態下SCLK時鍾選擇高電平還是低電平,根據上面的時序圖和DAC8501的數據手冊,兩種情況下都可以正常運行。經過實際測試,STM32F4使用這兩個配置確實都可以正常運行。程序里面默認是選擇CHOL = 0, CPHA = 1。
34.5.4 第4步:單SPI接口管理多個SPI設備的切換機制
單SPI接口管理多個SPI設備最麻煩的地方是不同設備的時鍾分配,時鍾極性和時鍾相位並不相同。對此的解決解決辦法是在片選階段配置切換,比如DAC8501的片選:
/* ********************************************************************************************************* * 函 數 名: DAC8501_SetCS1 * 功能說明: DAC8501 片選控制函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void DAC8501_SetCS1(uint8_t _Level) { if (_Level == 0) { bsp_SpiBusEnter(); /* 占用SPI總線 */ bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); CS1_0(); } else { CS1_1(); bsp_SpiBusExit(); /* 釋放SPI總線 */ } } /* ********************************************************************************************************* * 函 數 名: DAC8501_SetCS2(0) * 功能說明: 設置CS2。 用於運行中SPI共享。 * 形 參: 無 返 回 值: 無 ********************************************************************************************************* */ void DAC8501_SetCS2(uint8_t _level) { if (_level == 0) { bsp_SpiBusEnter(); /* 占用SPI總線 */ bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); CS2_0(); } else { CS2_1(); bsp_SpiBusExit(); /* 釋放SPI總線 */ } }
通過這種方式就有效的解決了單SPI接口管理多設備的問題。因為給每個設備都配了一個獨立的片選引腳,這樣就可以為每個設備都配置這么一個片選配置。
但是頻繁配置也比較繁瑣,所以函數bsp_InitSPIParam里面做了特別處理。當前配置與之前配置相同的情況下無需重復配置。
34.5.5 第5步:DAC8501的數據更新
DAC8501的雙通道數據更新通過下面的函數實現:
/* ********************************************************************************************************* * 函 數 名: DAC8501_SetDacData * 功能說明: 設置DAC數據 * 形 參: _ch, 通道, * _data : 數據 * 返 回 值: 無 ********************************************************************************************************* */ void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac) { uint32_t data; /* DAC8501.pdf page 12 有24bit定義 DB24:18 = xxxxx 保留 DB17: PD1 DB16: PD0 DB15:0 16位數據 其中 PD1 PD0 決定4種工作模式 0 0 ---> 正常工作模式 0 1 ---> 輸出接1K歐到GND 1 0 ---> 輸出100K歐到GND 1 1 ---> 輸出高阻 */ data = _dac; /* PD1 PD0 = 00 正常模式 */ if (_ch == 0) { DAC8501_SetCS1(0); } else { DAC8501_SetCS2(0); } /* DAC8501 SCLK時鍾高達30M,因此可以不延遲 */ g_spiLen = 0; g_spiTxBuf[g_spiLen++] = (data >> 16); g_spiTxBuf[g_spiLen++] = (data >> 8); g_spiTxBuf[g_spiLen++] = (data); bsp_spiTransfer(); if (_ch == 0) { DAC8501_SetCS1(1); } else { DAC8501_SetCS2(1); } }
函數實現比較簡單,每次更新發送24bit數據即可。
34.6 SPI總線板級支持包(bsp_spi_bus.c)
SPI總線驅動文件bsp_spi_bus.c主要實現了如下幾個API供用戶調用:
- bsp_InitSPIBus
- bsp_InitSPIParam
- bsp_spiTransfer
34.6.1 函數bsp_InitSPIBus
函數原型:
void bsp_InitSPIBus(void)
函數描述:
此函數主要用於SPI總線的初始化,在bsp.c文件調用一次即可。
34.6.2 函數bsp_InitSPIParam
函數原型:
void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
函數描述:
此函數用於SPI總線的配置。
函數參數:
- 第1個參數SPI總線的分頻設置,支持的參數如下:
SPI_BAUDRATEPRESCALER_2 2分頻
SPI_BAUDRATEPRESCALER_4 4分頻
SPI_BAUDRATEPRESCALER_8 8分頻
SPI_BAUDRATEPRESCALER_16 16分頻
SPI_BAUDRATEPRESCALER_32 32分頻
SPI_BAUDRATEPRESCALER_64 64分頻
SPI_BAUDRATEPRESCALER_128 128分頻
SPI_BAUDRATEPRESCALER_256 256分頻
- 第2個參數用於時鍾相位配置,支持的參數如下:
SPI_PHASE_1EDGE SCK引腳的第1個邊沿捕獲傳輸的第1個數據
SPI_PHASE_2EDGE SCK引腳的第2個邊沿捕獲傳輸的第1個數據
- 第3個參數是時鍾極性配置,支持的參數如下:
SPI_POLARITY_LOW SCK引腳在空閑狀態處於低電平
SPI_POLARITY_HIGH SCK引腳在空閑狀態處於高電平
34.6.3 函數bsp_spiTransfer
函數原型:
void bsp_spiTransfer(void)
函數描述:
此函數用於啟動SPI數據傳輸,支持查詢,中斷和DMA方式傳輸。
34.7 DAC8501支持包中斷方式(bsp_spi_dac8501.c)
DAC8501驅動文件bsp_spi_dac8501.c主要實現了如下幾個API供用戶調用:
- bsp_InitDAC8501
- DAC8501_SetCS1
- DAC8501_SetCS2
- DAC8501_SetDacData
- DAC8501_DacToVoltage
- DAC8501_VoltageToDac
34.7.1 函數bsp_InitDAC8501
函數原型:
void bsp_InitDAC8501(void)
函數描述:
主要用於DAC8501的初始化,調用前務必先調用函數bsp_InitSPIBus初始化SPI外設。
34.7.2 函數DAC8501_SetCS1
函數原型:
void DAC8501_SetCS1(uint8_t _Level)
函數描述:
此函數用於片選DAC8501模塊上的第1片8501。
函數參數:
- 第1個參數為0表示選中,為1表示取消選中。
34.7.3 函數DAC8501_SetCS2
函數原型:
void DAC8501_SetCS2(uint8_t _Level)
函數描述:
此函數用於片選DAC8501模塊上的第2片8501。
函數參數:
- 第1個參數為0表示選中,為1表示取消選中
34.7.4 函數DAC8501_SetDacData
函數原型:
void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
函數描述:
此函數用於設置DAC輸出,並立即更新。
函數參數:
- 第1個參數為0表示通道1,為1表示通道2。
- 第2個參數是DAC數值設置,范圍0到65535,0對應最小電壓值,65535對應最大電壓值。
34.7.5 函數DAC8501_DacToVoltage
函數原型:
int32_t DAC8501_DacToVoltage(uint16_t _dac)
函數描述:
此函數用於將DAC值換算為電壓值,單位0.1mV。
函數參數:
- 第1個參數DAC數值,范圍0到65535。
- 返回值,返回電壓值,單位0.1mV。
34.7.6 函數DAC8501_VoltageToDac
函數原型:
uint32_t DAC8501_VoltageToDac(int32_t _volt)
函數描述:
此函數用於將電壓值轉換為DAC值。
函數參數:
- 第1個參數是電壓值,范圍0到50000,單位0.1mV。
- 返回值,返回DAC值。
34.8 DAC8501驅動移植和使用
DAC8501移植步驟如下:
- 第1步:復制bsp_spi_bus.c,bsp_spi_bus.h,bsp_spi_dac8501.c,bsp_spi_dac8501.h到自己的工程目錄,並添加到工程里面。
- 第2步:根據使用的第幾個SPI,SPI時鍾,SPI引腳和DMA通道等,修改bsp_spi_bus.c文件開頭的宏定義
/* ********************************************************************************************************* * 時鍾,引腳,DMA,中斷等宏定義 ********************************************************************************************************* */ #define SPIx SPI1 #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE() #define DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE() #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET() #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET() #define SPIx_SCK_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define SPIx_SCK_GPIO GPIOB #define SPIx_SCK_PIN GPIO_PIN_3 #define SPIx_SCK_AF GPIO_AF5_SPI1 #define SPIx_MISO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define SPIx_MISO_GPIO GPIOB #define SPIx_MISO_PIN GPIO_PIN_4 #define SPIx_MISO_AF GPIO_AF5_SPI1 #define SPIx_MOSI_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define SPIx_MOSI_GPIO GPIOB #define SPIx_MOSI_PIN GPIO_PIN_5 #define SPIx_MOSI_AF GPIO_AF5_SPI1 #define SPIx_TX_DMA_CHANNEL DMA_CHANNEL_3 #define SPIx_TX_DMA_STREAM DMA2_Stream3 #define SPIx_RX_DMA_CHANNEL DMA_CHANNEL_3 #define SPIx_RX_DMA_STREAM DMA2_Stream0 #define SPIx_IRQn SPI1_IRQn #define SPIx_IRQHandler SPI1_IRQHandler #define SPIx_DMA_TX_IRQn DMA2_Stream3_IRQn #define SPIx_DMA_RX_IRQn DMA2_Stream0_IRQn #define SPIx_DMA_TX_IRQHandler DMA2_Stream3_IRQHandler #define SPIx_DMA_RX_IRQHandler DMA2_Stream0_IRQHandler
- 第3步:根據芯片支持的時鍾速度,時鍾相位和時鍾極性配置函數DAC8501_SetCS1和DAC8501_SetCS2。
/* ********************************************************************************************************* * 函 數 名: DAC8501_SetCS1 * 功能說明: DAC8501 片選控制函數 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void DAC8501_SetCS1(uint8_t _Level) { if (_Level == 0) { bsp_SpiBusEnter(); /* 占用SPI總線 */ bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); CS1_0(); } else { CS1_1(); bsp_SpiBusExit(); /* 釋放SPI總線 */ } } /* ********************************************************************************************************* * 函 數 名: DAC8501_SetCS2(0) * 功能說明: 設置CS2。 用於運行中SPI共享。 * 形 參: 無 返 回 值: 無 ********************************************************************************************************* */ void DAC8501_SetCS2(uint8_t _level) { if (_level == 0) { bsp_SpiBusEnter(); /* 占用SPI總線 */ bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); CS2_0(); } else { CS2_1(); bsp_SpiBusExit(); /* 釋放SPI總線 */ } }
- 第4步:根據使用的片選引腳,修改bsp_spi_dac8501.c文件開頭的宏定義。
#define CS1_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE() #define CS1_GPIO GPIOG #define CS1_PIN GPIO_PIN_10 #define CS1_1() CS1_GPIO->BSRR = CS1_PIN #define CS1_0() CS1_GPIO->BSRR = ((uint32_t)CS1_PIN << 16U) /* 特別注意,我們這里是用的擴展IO控制的 */ #define CS2_1() HC574_SetPin(NRF24L01_CE, 1); #define CS2_0() HC574_SetPin(NRF24L01_CE, 0);
- 第5步:初始化SPI。
/* 針對不同的應用程序,添加需要的底層驅動模塊初始化函數 */ bsp_InitSPIBus(); /* 配置SPI總線 */ bsp_InitDAC8501(); /* 初始化配置DAC8501 */
- 第6步:DAC8501驅動主要用到HAL庫的SPI驅動文件,簡單省事些可以添加所有HAL庫C源文件進來。
- 第7步:應用方法看本章節配套例子即可。
34.9 實驗例程設計框架
通過程序設計框架,讓大家先對配套例程有一個全面的認識,然后再理解細節,本次實驗例程的設計框架如下:
第1階段,上電啟動階段:
- 這部分在第14章進行了詳細說明。
第2階段,進入main函數:
- 第1部分,硬件初始化,主要是HAL庫,系統時鍾,滴答定時器和LED。
- 第2部分,應用程序設計部分,實現DAC8501的簡易信號發生器功能。
34.10 實驗例程說明(MDK)
配套例子:
V6-015_DAC8501簡易信號發生器(雙路輸出,16bit分辨率, 0-5V輸出)
實驗目的:
- 學習DAC8501的SPI DMA驅動方式實現。
實驗內容:
- DAC8501模塊上帶了兩片8501,每片是單通道DAC,片上輸出緩沖運放,軌到軌輸出,16bit分辨率,支持30MHz的SPI時鍾速度。
- DAC8501供電電壓2.7-5.5V,模擬輸出帶寬350KHz。
實驗操作:
- 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
- K1鍵按下,雙通道輸出,通道1輸出方波,通道2輸出正弦波。
- K2鍵按下,雙通道輸出方波。
- K3鍵按下,雙通道輸出正弦波。
- 搖桿OK鍵按下,雙通道輸出直流。
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1。
波形效果:
模塊插入位置:
程序設計:
系統棧大小分配:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* STM32F429 HAL 庫初始化,此時系統用的還是F429自帶的16MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到168MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V5開發板用戶手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化擴展IO */ bsp_InitLed(); /* 初始化LED */ BEEP_InitHard(); /* 初始化蜂鳴器 */ /* 針對不同的應用程序,添加需要的底層驅動模塊初始化函數 */ bsp_InitSPIBus(); /* 配置SPI總線 */ bsp_InitDAC8501(); /* 初始化配置DAC8501 */ }
主功能:
主程序實現如下操作:
- 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED4。
- K1鍵按下,雙通道輸出,通道1輸出方波,通道2輸出正弦波。
- K2鍵按下,雙通道輸出方波。
- K3鍵按下,雙通道輸出正弦波。
- 搖桿OK鍵按下,雙通道輸出直流。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ DemoSpiDac(); /* SPI DAC測試 */ } /* ********************************************************************************************************* * 函 數 名: DemoSpiDac * 功能說明: DAC8501測試 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void DemoSpiDac(void) { uint8_t i=0; uint8_t ucKeyCode; /* 按鍵代碼 */ sfDispMenu(); /* 打印命令提示 */ bsp_StartAutoTimer(0, 200); /* 啟動1個100ms的自動重裝的定時器 */ /* 生成方波數據 */ for(i =0; i< 50; i++) { ch1buf[i] = 0; } for(i =50; i< 100; i++) { ch1buf[i] = 65535; } /* 生成正弦波數據 */ MakeSinTable(ch2buf, 100, 0, 65535); /* 配置個TIM6中斷,頻率DAC_OUT_FREQ */ bsp_SetTIMforInt(TIM6, DAC_OUT_FREQ, 2, 0); while(1) { bsp_Idle(); /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */ /* 判斷定時器超時時間 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 進來一次 */ bsp_LedToggle(4); } /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */ ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下,雙通道輸出,通道1輸出方波,通道2輸出正弦波 */ /* 生成方波數據 */ for(i =0; i< 50; i++) { ch1buf[i] = 0; } for(i =50; i< 100; i++) { ch1buf[i] = 65535; } /* 生成正弦波數據 */ MakeSinTable(ch2buf, 100, 0, 65535); break; case KEY_DOWN_K2: /* K2鍵按下,雙通道輸出方波 */ /* 生成方波數據 */ for(i =0; i< 50; i++) { ch1buf[i] = 0; ch2buf[i] = 0; } for(i =50; i< 100; i++) { ch1buf[i] = 65535; ch2buf[i] = 65535; } break; case KEY_DOWN_K3: /* K3鍵按下,雙通道輸出正弦波 */ MakeSinTable(ch1buf, 100, 0, 65535); MakeSinTable(ch2buf, 100, 0, 65535); break; case JOY_DOWN_OK: /* 搖桿OK鍵按下,雙通道輸出直流 */ /* 通道1輸出-10V */ for(i = 0; i < 100; i++) { ch1buf[i] = 0; } /* 通道2輸出 10V */ for(i = 0; i < 100; i++) { ch2buf[i] = 65535; } break; default: /* 其它的鍵值不處理 */ break; } } } }
34.11 實驗例程說明(IAR)
配套例子:
V6-015_DAC8501簡易信號發生器(雙路輸出,16bit分辨率, 0-5V輸出)
實驗目的:
- 學習DAC8501的SPI DMA驅動方式實現。
實驗內容:
- DAC8501模塊上帶了兩片8501,每片是單通道DAC,片上輸出緩沖運放,軌到軌輸出,16bit分辨率,支持30MHz的SPI時鍾速度。
- DAC8501供電電壓2.7-5.5V,模擬輸出帶寬350KHz。
實驗操作:
- 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
- K1鍵按下,雙通道輸出,通道1輸出方波,通道2輸出正弦波。
- K2鍵按下,雙通道輸出方波。
- K3鍵按下,雙通道輸出正弦波。
- 搖桿OK鍵按下,雙通道輸出直流。
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1。
波形效果:
模塊插入位置:
程序設計:
系統棧大小分配:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* STM32F407 HAL 庫初始化,此時系統用的還是F407自帶的16MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到168MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V5開發板用戶手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化擴展IO */ bsp_InitLed(); /* 初始化LED */ BEEP_InitHard(); /* 初始化蜂鳴器 */ /* 針對不同的應用程序,添加需要的底層驅動模塊初始化函數 */ bsp_InitSPIBus(); /* 配置SPI總線 */ bsp_InitDAC8501(); /* 初始化配置DAC8501 */ }
主功能:
主程序實現如下操作:
- 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED4。
- K1鍵按下,雙通道輸出,通道1輸出方波,通道2輸出正弦波。
- K2鍵按下,雙通道輸出方波。
- K3鍵按下,雙通道輸出正弦波。
- 搖桿OK鍵按下,雙通道輸出直流。
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ DemoSpiDac(); /* SPI DAC測試 */ } /* ********************************************************************************************************* * 函 數 名: DemoSpiDac * 功能說明: DAC8501測試 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void DemoSpiDac(void) { uint8_t i=0; uint8_t ucKeyCode; /* 按鍵代碼 */ sfDispMenu(); /* 打印命令提示 */ bsp_StartAutoTimer(0, 200); /* 啟動1個100ms的自動重裝的定時器 */ /* 生成方波數據 */ for(i =0; i< 50; i++) { ch1buf[i] = 0; } for(i =50; i< 100; i++) { ch1buf[i] = 65535; } /* 生成正弦波數據 */ MakeSinTable(ch2buf, 100, 0, 65535); /* 配置個TIM6中斷,頻率DAC_OUT_FREQ */ bsp_SetTIMforInt(TIM6, DAC_OUT_FREQ, 2, 0); while(1) { bsp_Idle(); /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */ /* 判斷定時器超時時間 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 進來一次 */ bsp_LedToggle(4); } /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */ ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下,雙通道輸出,通道1輸出方波,通道2輸出正弦波 */ /* 生成方波數據 */ for(i =0; i< 50; i++) { ch1buf[i] = 0; } for(i =50; i< 100; i++) { ch1buf[i] = 65535; } /* 生成正弦波數據 */ MakeSinTable(ch2buf, 100, 0, 65535); break; case KEY_DOWN_K2: /* K2鍵按下,雙通道輸出方波 */ /* 生成方波數據 */ for(i =0; i< 50; i++) { ch1buf[i] = 0; ch2buf[i] = 0; } for(i =50; i< 100; i++) { ch1buf[i] = 65535; ch2buf[i] = 65535; } break; case KEY_DOWN_K3: /* K3鍵按下,雙通道輸出正弦波 */ MakeSinTable(ch1buf, 100, 0, 65535); MakeSinTable(ch2buf, 100, 0, 65535); break; case JOY_DOWN_OK: /* 搖桿OK鍵按下,雙通道輸出直流 */ /* 通道1輸出-10V */ for(i = 0; i < 100; i++) { ch1buf[i] = 0; } /* 通道2輸出 10V */ for(i = 0; i < 100; i++) { ch2buf[i] = 65535; } break; default: /* 其它的鍵值不處理 */ break; } } } }
34.12 總結
本章節涉及到的知識點非常多,需要大家稍花點精力去研究。