1. 程式人生 > >【菜鳥入門】stm32 之 ADC 模數轉換

【菜鳥入門】stm32 之 ADC 模數轉換

今天對ADC進行了研究,個人感覺,ADC的配置相對也對比較複雜一點,因為需要配置的暫存器是比較多的!

在datasheet 關於ADC的簡介中,明確說明ADC的輸入時鐘不得超過14M,他是有PCLK2經過分頻得來的

這次我們選用ADC1_IN0作為講解的物件,ADC1_IN0 -->PA0

所以在配置時鐘的時候要配置PA0和ADC1,關於怎麼配置,已經說的很清楚了。

在配置PA0的輸入模式方面我要說明一下,有好多人在這個地方還是很鬱悶的



在8.1.11 外設GPIO的配置中每種配置都說的很清楚,如果我們對那中模式配置有疑問,我們都可以直接在這裡查詢

這裡的ADC我們採用模擬輸入模式:


所以我們這裡需要把PA1配置為0000b

輸入配置完成,在開頭我已經強調過了,ADC的時鐘不能超過14MHz,所以我們要對ADC的時鐘進行些操作

RCC_CFGR這個暫存器找了我半天,我就感覺在RCC裡面配置,但是一直找不到ADC的配置項,後來在網上找了半天,才知道ADC的頻率配置在這裡選擇



我一般在配置系統時鐘的時候喜歡配置為72MHz,即PCLK2 = 72MHz

所以為了滿足我們的ADC,我們至少需要6分頻,當然8分頻也可以,好吧廢話了一句。。

我們這裡就6分頻吧:RCC->CFGR &= ~(3<<14);RCC->CRGR |= 2<<14;

這裡得到的ADC時鐘為12MHz,時鐘配置完成後,那我們就來專心配置ADC register了

當然,有經驗的人,不用想,直接先找到控制暫存器(CR ,Control Register)

ADC的控制暫存器比較多,我剛開始看的比較鬱悶,然後再李想老師的程式碼裡面找了一段(因為最近比較忙,沒有時間,所以喜歡搞定現成的程式碼研究下);當然大家也可以只要,只要你把別人的配置方法,配置原理學會了,也是很不錯的,有時候我道覺得這是一種比較學習的快捷辦法!這裡給大家交流下學習經驗

先看看ADCx_CR1


CR1暫存器大部分位是管理WATCH DOG的,我們一般不怎麼用WATCH DOG,在430上我基本上沒有用過看門狗,感覺這個狗不是很聽話,我也不是很瞭解他,所以以後用到了再說吧。

首先是雙模式選擇


我們需要注意下:在ADC2和ADC3中這些位為保留的,所以以後我們再配置的時候注意下,還有下面的幾行

這裡我們用獨立模式0000b,在此模式中,雙ADC同步不工作,每個ADC介面獨立工作;

關於模式大家可以看看11.9章

至今我還沒有明白這個掃描模式時幹嘛用的!誰會了,幫忙指點下,謝謝了。

關於CR2的配置相對比較多的!


我們這裡不使用外部事件來啟動轉換,直接用軟體來轉換,所以20位我們要置0,從而在選擇啟動規則通道組轉換的外部事件我們就只能用SWSTART(Software Start)


我們用的是ADC1,而且還關閉了外部啟動轉換,所以我們這裡就選擇111

為了保證資料資料的實時性,我們需要進行連續轉換,我不知道李想老師為什麼選用單次轉換,不過也無所謂了。

然後為了保證讀書的方便,我們可以把資料儲存的時候進行右對齊;這樣我們就不需要進行移位的操作了,直接讀就ok 了。

關於SQR暫存器,規則序列暫存器,聽著都糾結,我們只用一個通道,所以我們就二話不說的配置為0000b

通道的取樣時間,我的觀念是取樣時間越長越精確,經過測試確實是這樣

由於我們用的是CH1,所以我們呢就要配置SMPR的SMP1設定為111


這些配置完了,那我們就來啟動ADC吧。

還是CR2,開啟ADC,進行矯正復位,矯正。

完成後,就沒有了,只剩下讀資料了。

讀資料我要說下:


我們要先配置我們要取樣的通道,然後開啟控制暫存器CR2上的開始轉換 Start Conversion

注意檢測狀態暫存器裡面的轉換狀態,轉換結束,他會把EOC位置1

然後我們就可以讀資料了;

現在附上程式碼,大家可以參考程式碼看看:

/* adc.c */
#include <stm32f10x.h>

void adc1_init()
{
	RCC->APB2ENR |= 1<<2;
	GPIOA->CRL &= ~(0xf<<0);
	GPIOA->CRL |= 0x0<<0;
	
	RCC->APB2ENR |= 1<<9;
	RCC->APB2RSTR |= 1<<9;
	RCC->APB2RSTR &= ~(1<<9);
	
	RCC->CFGR &= ~(3<<14);
	RCC->CFGR |= 2<<14;	// 6 div PCLK2 / 6 = 12MHz
	
	ADC1->CR1 &= ~(0xf<<16);
	ADC1->CR1 |= 0<<16;			//Set Indenpendence Mode
	ADC1->CR1 &= ~(1<<8);		//Scan Mode Disable
	/*	Config Control Register 2*/
	ADC2->CR2 |= 1<<1;			//Continuous conversion mode
	
	ADC1->CR2 &= ~(7<<17);	//Clear
	ADC1->CR2 |= 7<<17;			//software start
	
	ADC1->CR2 |= 1<<20;			//Conversion on external event enable
	
	ADC1->CR2 &= ~(1<<11);	//Right Alignment
	
	ADC1->SQR1 &= ~(0xf<<20);
	ADC1->SQR1 |= 0<<20;
	ADC1->SMPR2 &= ~(0x7<<3);
	ADC1->SMPR2 |= 7<<3;
	
	ADC1->CR2 |= 1<<0;		//Start ADC to Calibration
	ADC1->CR2 |= 1<<3;
	while(ADC1->CR2 & 1<<3);
	ADC1->CR2 |= 1<<2;
	while(ADC1->CR2 & 1<<2);
}

unsigned short get_adc(unsigned char ch)
{
	ADC1->SQR3 &= ~(0xf<<0);
	ADC1->SQR3 |= ch;
	ADC1->CR2 |= 1<<22;
	while(!(ADC1->SR & 1<<1));
	return ADC1->DR;
}

主函式::::
/* main.c */
#include <stm32f10x.h>
#include "stdio.h"
#include "init.h"
#include "usart.h"
#include "adc.h"

int main()
{
	char buff[256];
	
	sys_init(9);
	rs232_init(CPU_72M,9600);
	rs232_send_byte('\n');
	adc1_init();
	while(1)
	{
		sprintf(buff,"V:%.3f V\n",3.3*get_adc(1)/4096);
		rs232_send_str(buff,strlen(buff));
		delay_ms(1000);
	}
}

ADC有的地方我還沒有搞的完全懂,願意聽各位大神指點!