本節來講述LPC1343內部ADC的使用。設計一個實驗,使用ADC的0通道進行AD轉換,并且將轉換結果通過UART發送在PC端的串口終端軟件觀察。
本文引用地址:http://www.104case.com/article/201611/320275.htm這次仍然以NXP提供的example作為例子,但是LPC1343內部ADC工作方式眾多,所以該example用了許多的預編譯結構,筆者在此將本次實驗不會用到的語句全部去掉,程序變得簡潔,也更易于理解。
同樣的,在此節中將不再將所用到的寄存器一一列出,而只是一個各個寄存器設置的“線索”,因為至此各個讀者一定已經擁有了自己翻閱用戶手冊查看對應寄存器內容的能力。
從主函數我們可以看出本次實驗的進展過程:
int main (void)
{
uint32_t i,j;
UARTInit(115200);//初始化UART
ADCInit( ADC_CLK );//初始化ADC
while(1)
{
ADCRead( 0 );//讀取0通道轉換值
while ( !ADCIntDone );//等待讀取完成
ADCIntDone = 0;//清除讀取完成標志
UARTBuffer[0]=(uint8_t)(*ADCValue>>8);//分離高2位數據
UARTBuffer[1]=(uint8_t)(*ADCValue);//分離低8位數據
UARTSend((uint8_t *)UARTBuffer, 2);//向UART發送數據
for(i=0;i<5000;i++)//延時
for(j=0;j<1000;j++);
}
}
大家應該在幾個8位單片機上都設計過這種ADC轉換程序,相信大家也肯定經歷過這個基礎的過程。
UART的初始化在上一節已經詳述了,我們直接來看看ADC的初始化ADCInit():
void ADCInit( uint32_t ADC_Clk )
{
LPC_SYSCON->;PDRUNCFG &= ~(0x1<<4);//打開ADC供電
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<13);//開啟ADC的AHB通道
LPC_IOCON->JTAG_TDI_PIO0_11 = 0x02;//選擇ADC0引腳功能
LPC_ADC->CR = ((SystemCoreClock/LPC_SYSCON->SYSAHBCLKDIV)/ADC_Clk-1)<<8;
#if ADC_INTERRUPT_FLAG//使用ADC中斷功能
NVIC_EnableIRQ(ADC_IRQn);
LPC_ADC->INTEN = 0x1FF;
#endif
return;
}
1、該函數唯一的參數填入欲設置的ADC工作速率,單位是Hz,本次實驗填入4500000,即4.5MHz;
2、ADC初始化不同于前面其他設備的一個地方,在于它的電源是默認關閉的,所以首先要打開它的電源,明顯是一個降低功耗的措施;
3、ADC的工作速率,從用戶手冊可以查看到如下描述:
The APB clock (PCLK) is divided by CLKDIV +1 to produce the clock for the ADC, which should be less than or equal to 4.5 MHz.由此我們知道該ADC的最大驅動時鐘頻率是4.5MHz。同時分頻數是CLKDIV +1,所以程序中要“ADC_Clk-1”,至于為什么要左移8位呢?那是因為設置的分頻數是存放于ADCCR(ADC控制寄存器)中的第8:15位。
4、如果使用中斷功能,則除了要設置NVIC控制器之外,還需要在ADCINTEN中打開各個通道的中斷功能,當某通道轉換完成時,會觸發對于中斷。
初始化完畢之后,就可以開啟ADC進行轉換了。首先是ADCRead():
uint32_t ADCRead( uint8_t channelNum )
{
if ( channelNum >= ADC_NUM )//判斷通道號是否過大
{
channelNum = 0;//復位通道號
}
LPC_ADC->CR &= 0xFFFFFF00; //清除所有轉換通道的上一次選擇狀態
LPC_ADC->CR |= (1 << 24) | (1 << channelNum);//選擇通道,并開啟轉換
return ( channelNum );//返回通道號
}
1、本函數要求填入的唯一參數是所希望進行ADC的通道號;
2、轉換前應該清除上一次通道被選狀態;
3、在CR控制器中的26:24位控制著ADC的多種轉換啟動方式,本實驗中使用最普通的一種:立即開始轉換。
4、因為本實驗使用了ADC的中斷功能,轉換結果在ADC中斷中存儲,所以此函數在使用ADC中斷功能的情況下返回的是轉換的通道號。
ADC中斷功能的情況下返回的是轉換的通道號。
所以當本函數運行結束之后,ADC轉換開始,等待進入中斷服務函數:
void ADC_IRQHandler (void)
{
uint32_t regVal;
LPC_ADC->CR &= 0xF8FFFFFF;
regVal = LPC_ADC->STAT;
if ( regVal & 0x0000FF00 )
{
regVal = (regVal & 0x0000FF00) >> 0x08;//如果有,確認通道
switch ( regVal )//通過讀取轉換值來清除ADC數據,注意這部分是錯誤數據,是丟棄的
{
case 0x01:regVal = LPC_ADC->DR0;break;
case 0x02:regVal = LPC_ADC->DR1;break;
case 0x04:regVal = LPC_ADC->DR2;break;
case 0x08:regVal = LPC_ADC->DR3;break;
case 0x10:regVal = LPC_ADC->DR4;break;
case 0x20:regVal = LPC_ADC->DR5;break;
case 0x40:regVal = LPC_ADC->DR6;break;
case 0x80:regVal = LPC_ADC->DR7;break;
default:break;
}
LPC_ADC->CR &= 0xF8FFFFFF;
ADCIntDone = 1;
return;
}
if ( regVal & ADC_ADINT )//判斷是否有任何一個通道轉換結束
{
switch ( regVal & 0xFF )
{
case 0x01:ADCValue[0] = ( LPC_ADC->DR0 >> 6 ) & 0x3FF;break;
case 0x02:ADCValue[1] = ( LPC_ADC->DR1 >> 6 ) & 0x3FF;break;
case 0x04:ADCValue[2] = ( LPC_ADC->DR2 >> 6 ) & 0x3FF;break;
case 0x08:ADCValue[3] = ( LPC_ADC->DR3 >> 6 ) & 0x3FF;break;
case 0x10:ADCValue[4] = ( LPC_ADC->DR4 >> 6 ) & 0x3FF;break;
case 0x20:ADCValue[5] = ( LPC_ADC->DR5 >> 6 ) & 0x3FF;break;
case 0x40:ADCValue[6] = ( LPC_ADC->DR6 >> 6 ) & 0x3FF;break;
case 0x80:ADCValue[7] = ( LPC_ADC->DR7 >> 6 ) & 0x3FF;break;
default:break;
}
ADCIntDone = 1;//讀取結束標志
}
return;
}
1、進入中斷服務程序之后,首先停止AD轉換;
2、和UART一樣,ADC中斷標志也是通過讀取來清除的;
3、首先要檢查溢出錯誤,如果有,則數據無效,要通道讀取來清除ADC轉換數據寄存器(ADCDR);
4、ADC中斷有兩種,一種是任何一個通道完成轉換都會觸發,一種是某個中斷完成轉換就會觸發,本實驗中兩種中斷都打開了,因此先判斷是否有轉換完成,再判斷是哪個通道完成轉換;
中斷函數的結束意味著讀取完成,剩下的就是將讀出數據發送到UART去顯示了。但在這之前,因為LPC1343的ADC默認情況下是10位精度,而我們的UART是以字符為數據長度發送的,所以筆者特意將轉換結果轉換成了16位長度,分兩次發送。現將本次實驗運行過程概況如下:
UART初始化——ADC初始化——開始轉換——轉換結束觸發中斷——判斷有無錯誤——有錯誤則放棄無效數據,無錯誤則讀出有效數據——數據處理——發至UART
附上運行結果jpg兩張,第一張,0通道引腳接在GND:

第二張,0通道接在VCC 3.3:


理論上3.3V為滿賦值,轉換結果應該是是11 1111 1111=0x3ff,不過實際并非如此,說明其實我們板子上引出的電源還是有一定波動的。
評論