基於stm32f103的adc採集+dma傳輸實驗
一.adc配置:設置規則序列
typedef struct
{
uint32_t ADC_Mode; // adc模式位(具體參考不完全手冊)
FunctionalState ADC_ScanConvMode; // 用來設置是否開啓掃描模式(disable關閉,ENABLE開啓掃描)
FunctionalState ADC_ContinuousConvMode; // 用來設置是否開啓連續轉換模式(disable關閉,ENABLE開啓掃描)
uint32_t ADC_ExternalTrigConv; // 用來設置啓動規則轉換組轉換的外部事件,軟件觸發選ADC_ExternalTrigConv_None 即可
uint32_t ADC_DataAlign; // 數據對其方式(左對齊,右對齊)
uint8_t ADC_NbrOfChannel; // 設置規則序列長度 (在多通道掃描模式下需要給每個通道設置執行順序這個順序就是根據規則序列表裏面排列的通道順序來執行的因此我們要設置這個表裏面有多少個通道)
}ADC_InitTypeDef;
adc配置流程:
1.使能io時鐘和adc時鐘(不用使能端口複用功能,adc掛載在APB2總線下,只有在其他總線下的外外設才需要端口複用)
2.設置adc時鐘分配因子以確定adc工作時鐘頻率。
3.重置adc寄存器。
4.初始化adc結構體參數。(ADC_Init())
5.使能adc。
6.開啓復位校準。
7.開啓ad校準。
最後一步
8.爲通道設置採樣時間(如果是多通道則需要配置每個通道的採樣優先級即:規則序列表)
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
注意:轉換時間=採樣事件+12.5週期
若ADC_CLOCK=14M,採樣時間1.5週期那麼轉換時間爲1m即是1us(ADC_CLOCK最高頻率不能超過14m一般設置6分頻即72M/6=12M)。
二.dma配置步驟:
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //dma外設地址(u32類型例如:(u32)&(ADC1->DR))
uint32_t DMA_MemoryBaseAddr; //內存地址(u32類型例如:(u32)a(數組名)
uint32_t DMA_DIR; // 傳輸方向(內存--外設,外設---內存,內存---內存)
uint32_t DMA_BufferSize; //一次傳輸數據量的大小
uint32_t DMA_PeripheralInc; //傳輸數據的時候外設地址是不變還是遞增
uint32_t DMA_MemoryInc; //傳輸數據的時候內存地址是不變還是遞增
uint32_t DMA_PeripheralDataSize; //外設的的數據長度是爲字節傳輸(8bits),半字傳輸 (16bits)還是字傳輸 (32bits)
uint32_t DMA_MemoryDataSize; //內存的的數據長度是爲字節傳輸(8bits),半字傳輸 (16bits)還是字傳輸 (32bits)
uint32_t DMA_Mode; //設置 DMA 模式是否循環採集(可選單詞采集和循環採集)
uint32_t DMA_Priority; //各個通道優先級
uint32_t DMA_M2M; //是 否 是 存 儲 器 到 存 儲 器 模 式 傳 輸 ,
}DMA_InitTypeDef;
注意 :DMA_MemoryDataSize和DMA_PeripheralDataSize庫函數設置的時候兩個的值不同。
例如:#define DMA_PeripheralDataSize_Word ((uint32_t)0x00000200)
#define DMA_MemoryDataSize_Word ((uint32_t)0x00000800)
dma配置步驟:
1.使能相應時鐘
2.將dma相應通道寄存器值設置爲缺省值
3.初始化結構體參數
4.初始化dma
5.使能dma
到這裏已經配置完dma最後一步:
6.使能相應外設dma(例如:ADC_DMACmd(ADC1,ENABLE);只用使能一次)
完整代碼:
int DMA_Config(DMA_Channel_TypeDef* CHx,u32 PBaseAddr,u32 MBaseAddr,u32 buffsize)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_DeInit(CHx);
DMA_InitStruct.DMA_PeripheralBaseAddr=PBaseAddr; //外設基地址
DMA_InitStruct.DMA_MemoryBaseAddr=MBaseAddr; //內存寄存器
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC; //數據傳輸方向 外設到內存
DMA_InitStruct.DMA_BufferSize=32; //通道緩存大小
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外設寄存器地址不遞增
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Disable; //關閉內存寄存器地址遞增
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //外圍數據寬度32位
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Word; //內存數據寬度
DMA_InitStruct.DMA_Mode=DMA_Mode_Circular; //循環傳輸模式
DMA_InitStruct.DMA_Priority=DMA_Priority_High; //優先級
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable; //不開啓內存到內存
DMA_Init(CHx ,&DMA_InitStruct); //初始化DMA
//USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
DMA_Cmd(CHx ,ENABLE);
return 1;
}
void Adc_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);//使能時鐘
//配置GPIOA_5口模擬輸入
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//ADC時鐘分頻因子
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//重置ADC寄存器
ADC_DeInit(ADC1);
ADC_InitStruct.ADC_Mode= ADC_Mode_Independent;//獨立模式
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right; //數據右對齊
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None ; //無外部觸發
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //enable開啓連續掃描
ADC_InitStruct.ADC_NbrOfChannel=1; //規則轉換通道數目
ADC_InitStruct.ADC_ScanConvMode=DISABLE; //DISABLE單通道模式,enable多通道掃描模式
ADC_Init(ADC1,&ADC_InitStruct);
ADC_Cmd(ADC1,ENABLE); //使能adc
ADC_ResetCalibration(ADC1);//開啓復位校準
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1); //開啓ad校準
while(ADC_GetCalibrationStatus(ADC1));
}
u16 Get_Adc(u8 ch1,u8 ch2)
{
u16 count=0;
ADC_RegularChannelConfig(ADC1,ch1,1,ADC_SampleTime_239Cycles5);
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
{
count++;
if(count==60000)
break;
}
return ADC_GetConversionValue(ADC1); //返回adc數據寄存器器的值
注意:由於將 DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //外圍數據寬度32位
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Word; //內存數據寬度
改爲: DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //外圍數據寬度32位
DMA_InitStruct.DMA_MemoryDataSize=DMA_PeripheralDataSize_HalfWord; //內存數據寬度
這樣接收到的數據只有8位也就是最大值255
注意:在DMA傳輸過程中使用內存地址遞增模式,循環傳輸時到傳輸到數組結尾時返回的位置是傳入的地址處,而不是數組首地址。
例如:
int a[10];
DMA_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)(&a[1]),(u32)buffsize); //dma初始化注意此時傳入的地址是a+1
當dma傳輸數據到a[10]後繼續從a[1]地址處繼續傳輸,而不是從數組首地址a[0]處。。。