這是一個綜合的例子,演示了ADC模塊、DMA模塊和USART模塊的基本使用。
我們在這里設置ADC為連續(xù)轉(zhuǎn)換模式,常規(guī)轉(zhuǎn)換序列中有兩路轉(zhuǎn)換通道,分別是ADC_CH10(PC0)和ADC_CH16(片內(nèi)溫度傳感器)。因為使用了自動多通道轉(zhuǎn)換,數(shù)據(jù)的取出工作最適合使用DMA方式取出,so,我們在內(nèi)存里開辟了一個u16 AD_Value[2]數(shù)組,并設置了相應的DMA模塊,使ADC在每個通道轉(zhuǎn)換結(jié)束后啟動DMA傳輸,其緩沖區(qū)數(shù)據(jù)量為2個HalfWord,使兩路通道的轉(zhuǎn)換結(jié)果自動的分別落到AD_Value[0]和AD_Value[1]中。
然后,在主函數(shù)里,就無需手動啟動AD轉(zhuǎn)換,等待轉(zhuǎn)換結(jié)束,再取結(jié)果了。我們可以在主函數(shù)里隨時取AD_Value中的數(shù)值,那里永遠都是最新的AD轉(zhuǎn)換結(jié)果。
如果我們定義一個更大的AD_Value數(shù)組,并調(diào)整DMA的傳輸數(shù)據(jù)量(BufferSize)可以實現(xiàn)AD結(jié)果的循環(huán)隊列存儲,從而可以進行各種數(shù)字濾波算法。
接著,取到轉(zhuǎn)換結(jié)果后,根據(jù)V=(AD_Value/4096)*Vref+的公式可以算出相應通道的電壓值,也可以根據(jù) T(℃) = (1.43 - Vad)/34*10^(-6) + 25的算法,得到片內(nèi)溫度傳感器的測量溫度值了。
通過重新定義putchar函數(shù),及包含"stdio.h"頭文件,我們可以方便的使用標準C的庫函數(shù)printf(),實現(xiàn)串口通信。
相關的官方例程,可以參考FWLib V2.0的ADCADC1_DMA和USARTprintf兩個目錄下的代碼。
本代碼例子是基于STM32F103VBT6
/******************************************************************************
* 本文件實現(xiàn)ADC模塊的基本功能
* 設置ADC1的常規(guī)轉(zhuǎn)換序列包含CH10和CH16(片內(nèi)溫度傳感器)
* 設置了連續(xù)轉(zhuǎn)換模式,并使用DMA傳輸
* AD轉(zhuǎn)換值被放在了AD_Value[2]數(shù)組內(nèi),[0]保存CH0結(jié)果,[1]保存CH16結(jié)果
* GetVolt函數(shù)計算[0]的值對應的電壓值(放大100倍,保留2位小數(shù))
* GetTemp函數(shù)計算[1]的值對應的溫度值,計算公式在相應函數(shù)內(nèi)有說明
* 作者:jjldc(九九)
*******************************************************************************/
#include "stm32f10x_lib.h"
#include "stdio.h"
#define ADC1_DR_Address ((u32)0x4001244C)
vu16 AD_Value[2];
vu16 i=0;
s16 Temp;
u16 Volt;
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART1_Configuration(void);
void ADC1_Configuration(void);
void DMA_Configuration(void);
int fputc(int ch, FILE *f);
void Delay(void);
u16 GetTemp(u16 advalue);
u16 GetVolt(u16 advalue);
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
USART1_Configuration();
DMA_Configuration();
ADC1_Configuration();
//啟動第一次AD轉(zhuǎn)換
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
//因為已經(jīng)配置好了DMA,接下來AD自動連續(xù)轉(zhuǎn)換,結(jié)果自動保存在AD_Value處
while (1)
{
Delay();
Temp = GetTemp(AD_Value[1]);
Volt = GetVolt(AD_Value[0]);
USART_SendData(USART1, 0x0c); //清屏
//注意,USART_SendData函數(shù)不檢查是否發(fā)送完成
//等待發(fā)送完成
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
printf("電壓:%d.%dt溫度:%d.%d℃rn",
Volt/100, Volt%100, Temp/100, Temp%100);
}
}
int fputc(int ch, FILE *f)
{
//USART_SendData(USART1, (u8) ch);
USART1->DR = (u8) ch;
/* Loop until the end of transmission */
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{
}
return ch;
}
void Delay(void)
{
u32 i;
for(i=0;i<0x4f0000;i++);
return;
}
/*******************************************************************************
* Function Name : GetTemp
* Description : 根據(jù)ADC結(jié)果計算溫度
* Input : u16 advalue
* Output :
* Return : u16 temp
*******************************************************************************/
u16 GetTemp(u16 advalue)
{
u32 Vtemp_sensor;
s32 Current_Temp;
// ADC轉(zhuǎn)換結(jié)束以后,讀取ADC_DR寄存器中的結(jié)果,轉(zhuǎn)換溫度值計算公式如下:
// V25 - VSENSE
// T(℃) = ------------ + 25
// Avg_Slope
// V25: 溫度傳感器在25℃時 的輸出電壓,典型值1.43 V。
// VSENSE:溫度傳感器的當前輸出電壓,與ADC_DR 寄存器中的結(jié)果ADC_ConvertedValue之間的轉(zhuǎn)換關系為:
// ADC_ConvertedValue * Vdd
// VSENSE = --------------------------
// Vdd_convert_value(0xFFF)
// Avg_Slope:溫度傳感器輸出電壓和溫度的關聯(lián)參數(shù),典型值4.3 mV/℃。
Vtemp_sensor = advalue * 330 / 4096;
Current_Temp = (s32)(143 - Vtemp_sensor)*10000/43 + 2500;
return (s16)Current_Temp;
}
/*******************************************************************************
* Function Name : GetVolt
* Description : 根據(jù)ADC結(jié)果計算電壓
* Input : u16 advalue
* Output :
* Return : u16 temp
*******************************************************************************/
u16 GetVolt(u16 advalue)
{
return (u16)(advalue * 330 / 4096);
}
/*******************************************************************************
* Function Name : RCC_Configuration
* Description : 系統(tǒng)時鐘設置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;
//使能外部晶振
RCC_HSEConfig(RCC_HSE_ON);
//等待外部晶振穩(wěn)定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
//如果外部晶振啟動成功,則進行下一步操作
if(HSEStartUpStatus==SUCCESS)
{
//設置HCLK(AHB時鐘)=SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//PCLK1(APB1) = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
//PCLK2(APB2) = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//設置ADC時鐘頻率
RCC_ADCCLKConfig(RCC_PCLK2_Div2);
//FLASH時序控制
//推薦值:SYSCLK = 0~24MHz Latency=0
// SYSCLK = 24~48MHz Latency=1
// SYSCLK = 48~72MHz Latency=2
FLASH_SetLatency(FLASH_Latency_2);
//開啟FLASH預取指功能
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//PLL設置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//啟動PLL
RCC_PLLCmd(ENABLE);
//等待PLL穩(wěn)定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
//系統(tǒng)時鐘SYSCLK來自PLL輸出
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//切換時鐘后等待系統(tǒng)時鐘穩(wěn)定
while(RCC_GetSYSCLKSource()!=0x08);
}
//下面是給各模塊開啟時鐘
//啟動GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,
ENABLE);
//啟動AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//啟