這是一個(gè)綜合的例子,演示了ADC模塊、DMA模塊和USART模塊的基本使用。
我們?cè)谶@里設(shè)置ADC為連續(xù)轉(zhuǎn)換模式,常規(guī)轉(zhuǎn)換序列中有兩路轉(zhuǎn)換通道,分別是ADC_CH10(PC0)和ADC_CH16(片內(nèi)溫度傳感器)。因?yàn)槭褂昧俗詣?dòng)多通道轉(zhuǎn)換,數(shù)據(jù)的取出工作最適合使用DMA方式取出,so,我們?cè)趦?nèi)存里開(kāi)辟了一個(gè)u16 AD_Value[2]數(shù)組,并設(shè)置了相應(yīng)的DMA模塊,使ADC在每個(gè)通道轉(zhuǎn)換結(jié)束后啟動(dòng)DMA傳輸,其緩沖區(qū)數(shù)據(jù)量為2個(gè)HalfWord,使兩路通道的轉(zhuǎn)換結(jié)果自動(dòng)的分別落到AD_Value[0]和AD_Value[1]中。
然后,在主函數(shù)里,就無(wú)需手動(dòng)啟動(dòng)AD轉(zhuǎn)換,等待轉(zhuǎn)換結(jié)束,再取結(jié)果了。我們可以在主函數(shù)里隨時(shí)取AD_Value中的數(shù)值,那里永遠(yuǎn)都是最新的AD轉(zhuǎn)換結(jié)果。
如果我們定義一個(gè)更大的AD_Value數(shù)組,并調(diào)整DMA的傳輸數(shù)據(jù)量(BufferSize)可以實(shí)現(xiàn)AD結(jié)果的循環(huán)隊(duì)列存儲(chǔ),從而可以進(jìn)行各種數(shù)字濾波算法。
接著,取到轉(zhuǎn)換結(jié)果后,根據(jù)V=(AD_Value/4096)*Vref+的公式可以算出相應(yīng)通道的電壓值,也可以根據(jù) T(℃) = (1.43 - Vad)/34*10^(-6) + 25的算法,得到片內(nèi)溫度傳感器的測(cè)量溫度值了。
通過(guò)重新定義putchar函數(shù),及包含"stdio.h"頭文件,我們可以方便的使用標(biāo)準(zhǔn)C的庫(kù)函數(shù)printf(),實(shí)現(xiàn)串口通信。
相關(guān)的官方例程,可以參考FWLib V2.0的ADCADC1_DMA和USARTprintf兩個(gè)目錄下的代碼。
本代碼例子是基于STM32F103VBT6
/******************************************************************************
* 本文件實(shí)現(xiàn)ADC模塊的基本功能
* 設(shè)置ADC1的常規(guī)轉(zhuǎn)換序列包含CH10和CH16(片內(nèi)溫度傳感器)
* 設(shè)置了連續(xù)轉(zhuǎn)換模式,并使用DMA傳輸
* AD轉(zhuǎn)換值被放在了AD_Value[2]數(shù)組內(nèi),[0]保存CH0結(jié)果,[1]保存CH16結(jié)果
* GetVolt函數(shù)計(jì)算[0]的值對(duì)應(yīng)的電壓值(放大100倍,保留2位小數(shù))
* GetTemp函數(shù)計(jì)算[1]的值對(duì)應(yīng)的溫度值,計(jì)算公式在相應(yīng)函數(shù)內(nèi)有說(shuō)明
* 作者: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();
//啟動(dòng)第一次AD轉(zhuǎn)換
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
//因?yàn)橐呀?jīng)配置好了DMA,接下來(lái)AD自動(dòng)連續(xù)轉(zhuǎn)換,結(jié)果自動(dòng)保存在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é)果計(jì)算溫度
* 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)換溫度值計(jì)算公式如下:
// V25 - VSENSE
// T(℃) = ------------ + 25
// Avg_Slope
// V25: 溫度傳感器在25℃時(shí) 的輸出電壓,典型值1.43 V。
// VSENSE:溫度傳感器的當(dāng)前輸出電壓,與ADC_DR 寄存器中的結(jié)果ADC_ConvertedValue之間的轉(zhuǎn)換關(guān)系為:
// ADC_ConvertedValue * Vdd
// VSENSE = --------------------------
// Vdd_convert_value(0xFFF)
// Avg_Slope:溫度傳感器輸出電壓和溫度的關(guān)聯(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é)果計(jì)算電壓
* Input : u16 advalue
* Output :
* Return : u16 temp
*******************************************************************************/
u16 GetVolt(u16 advalue)
{
return (u16)(advalue * 330 / 4096);
}
/*******************************************************************************
* Function Name : RCC_Configuration
* Description : 系統(tǒng)時(shí)鐘設(shè)置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;
//使能外部晶振
RCC_HSEConfig(RCC_HSE_ON);
//等待外部晶振穩(wěn)定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
//如果外部晶振啟動(dòng)成功,則進(jìn)行下一步操作
if(HSEStartUpStatus==SUCCESS)
{
//設(shè)置HCLK(AHB時(shí)鐘)=SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//PCLK1(APB1) = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
//PCLK2(APB2) = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//設(shè)置ADC時(shí)鐘頻率
RCC_ADCCLKConfig(RCC_PCLK2_Div2);
//FLASH時(shí)序控制
//推薦值:SYSCLK = 0~24MHz Latency=0
// SYSCLK = 24~48MHz Latency=1
// SYSCLK = 48~72MHz Latency=2
FLASH_SetLatency(FLASH_Latency_2);
//開(kāi)啟FLASH預(yù)取指功能
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//PLL設(shè)置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//啟動(dòng)PLL
RCC_PLLCmd(ENABLE);
//等待PLL穩(wěn)定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
//系統(tǒng)時(shí)鐘SYSCLK來(lái)自PLL輸出
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//切換時(shí)鐘后等待系統(tǒng)時(shí)鐘穩(wěn)定
while(RCC_GetSYSCLKSource()!=0x08);
}
//下面是給各模塊開(kāi)啟時(shí)鐘
//啟動(dòng)GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,
ENABLE);
//啟動(dòng)AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//啟