娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

歷史上的今天

今天是:2024年09月07日(星期六)

2020年09月07日 | AVR單片機教程——ADC

發布者:omicron25 來源: eefocus關鍵字:AVR  單片機  ADC 手機看文章 掃描二維碼
隨時隨地手機看文章

ADC

計算機的世界是0和1的。單片機可以通過讀取0和1來確定按鍵狀態,也可以輸出0和1來控制LED。即使是看起來不太0和1的PWM,好像可以輸出0到5V之間的電壓一樣,達到0和1之間的效果,但本質上還是高低電平。


但是,世界上終究還是有0和1無法表示的。如果引腳上被施加0到5V之間的電壓,寄存器PINx無法告訴我們具體情況,只能指示這個電壓是1.5V以下還是3V以上(參考數據手冊“Electrical characteristics”)。這種可以連續變化的信號稱為模擬信號,與離散的、只能取0或1(0或5V)的數字信號對立。


這并不代表數字世界無法處理模擬信號,相反,一種相當常用的處理模擬信號的方法,就是把模擬信號轉換成數字信號,用處理器來運算,然后再轉換成模擬信號。這個過程中涉及到模擬-數字轉換和數字-模擬轉換,分別需要ADC和DAC來實現。大多數單片機,作為現實世界中的工具,需要接觸模擬信號,尤其是模擬信號的輸入,會集成ADC。


ADC的一個參數是分辨率,指它的位數,反映了可以產生的不同輸出的數量(8位ADC可以產生0~255的值)與量化最小物理量(通常是電壓)的能力(比如當參考電壓為2.56V時,理想情況下,8位ADC可以分辨兩個相差0.01V的電壓的不同)。AVR單片機帶有的ADC是10位的。


另一個參數是轉換速率,每秒進行A/D轉換的次數。AVR單片機的ADC為了達到10位分辨率的精度,最大轉換速率為15kSPS(千次采樣每秒)。如果可以接受較低的精度,也可以以200kSPS采樣,獲得8位數據。


分辨率與精度是不同的概念。在這篇入門級教程中,我們只需要知道,A/D轉換是會有誤差的(數據手冊23.7.4一節介紹了可能的誤差來源)。即使是相同的電壓,兩次測量的結果也可能是不同的。


要進行A/D轉換,需要提供參考電壓和待測電壓,轉換的結果為待測電壓參考電壓×2分辨率。寄存器ADMUX中的ADLAR位控制轉換結果的對齊方式。當右對齊時,公式中分辨率取10,轉換結果在16位寄存器ADC中(實際上是兩個8位寄存器ADCH與ADCL,但程序可以直接使用ADC,編譯器會處理好一些注意事項);當左對齊時,分辨率取8,轉換結果在ADCH中。可以直接把ADC當做16位寄存器,編譯器會處理好一些注意事項。


ADC有4種參考電壓可供選擇,分別是AREF、AVCC(5V)、1.1V和2.56V,由REFS1:0選擇。8個單端端口(開發板上引出了4個,端口0到3),以及一些差分端口(1x、10x、200x增益)和兩個參考電壓,共32個通道,可以通過多路復用器連接到ADC上進行轉換,由MUX4:0選擇。注意,ADC只有一個,在同一時刻只能轉換一個通道的電壓。


ADCSRA和ADCSRB用于控制A/D轉換。ADCSRA中ADEN啟用ADC組件,ADSC位啟動一次轉換,到ADIF位為1時轉換結束,需要寫1才能清零。ADPS2:0選擇ADC時鐘分頻系數,這關系到轉換速率:首次采樣(啟用ADC后第一次或同時)需要25個ADC時鐘周期,隨后每次采樣需要13個。ADCSRB可以選擇A/D轉換觸發源。


開發板提供了3.3V電源,可用于給只支持3.3V的設備供電。我們用ADC來測量這個電壓,然后在串口上輸出。


#include

#include


int main()

{

    uart_init(UART_TX);

    ADMUX  =    0b01 << REFS0  // AVCC as reference

           |     0b0 << ADLAR  // right adjust

           | 0b00000 << MUX0;  // ADC0 single ended

    ADCSRA =       1 << ADEN   // enable ADC

           |       1 << ADSC   // start conversion

           |       1 << ADIF   // clear flag

           |   0b111 << ADPS0; // divide by 128

           

    while (!(ADCSRA & 1 << ADIF)) // wait until flag is set

        ;

    

    uint16_t voltage = (uint32_t)ADC * 500 >> 10; // ADC / 1024 * 500 (* 10mV)

    uint8_t integer = 0;                          // integer part of voltage

    while (integer * 100 <= voltage)              // calculate integer part

        ++integer;

    --integer;

    uint8_t decimal = voltage - integer * 100;    // calculate decimal part

    

    uart_print_int(integer);                      // print the voltage

    uart_print_char('.');

    uart_set_align(UART_ALIGN_RIGHT, 2, '0');

    uart_print_int(decimal);

    uart_print_string("Vn");

    

    while (1)

        ;

}

數據手冊28.8節指明,當ADC時鐘為200kHz時,ADC絕對精度可以達到1.9LSB(1LSB就是1024中的1)。經計算得,為了使ADC時鐘不超過這個速率,分頻系數應該取128。


所測電壓為voltage=ADC1024×5V,但直接這樣計算會涉及到浮點運算,而AVR硬件不支持浮點,所有浮點運算都是軟件實現的,速度相當慢,兩個float相乘需要1000多個指令周期,除法需要更多,都是應該竭力避免的。盡管最后的電壓是一個小數,但可以通過移動小數點把它變成整數。5V參考電壓下,精度1.9LSB約為9.28mV,因此右移兩位,以10mV為單位計算。先算乘法以避免浮點除法,算式變為voltage=ADC×5001024。


ADC的值直接與500相乘會溢出,因此需要先提升為uint32_t。當然,你可以把算式約分一下,但不改變會溢出的事實。盡管32位整數不太好處理,但相比浮點數還是容易得多。然后是一個除法。16位整數除法需要173個CPU指令周期(參考:Multiply and Divide Routines),是比較耗時的。盡管這個程序中只計算一次,但還是應該盡量想辦法避免耗時的操作。注意到除數1024是一個特殊的數,是2的10次方,可以通過移位運算來做除法,而移位運算相比除法快得多(也許編譯器會把/ 1024優化成>> 10)。


然后我們需要把這個數的百位部分拿出來作電壓的整數部分,十位和個位作小數部分,可以通過除以100和模100來實現。由于這里的100是一個編譯期常數,編譯期很可能把這個除法和取模優化掉,不調用100多周期的過程。這里我們感受一下手動優化。由于變量voltage一定小于500,可以用乘法和比較的循環來試出這個商,其中乘法的執行次數不超過6次——AVR單片機有雙周期乘法指令。然后,用乘法與減法求出余數。


ADC是單片機編程中相對容易用到浮點與乘除法的場合,設計算法時應盡量注意避免耗時的運算,或手動編寫優化的算法來代替。


電位器

電位器,開發板右側兩個旋鈕中左邊一個,可以連續轉動300°。電氣屬性相當于物理實驗中的滑動變阻器,如果把兩個定片接在VCC和GND上,動片電壓就可以指示旋鈕旋轉的角度,并且通常與角度是成正比的。


之前提到過,A/D轉換是有誤差的,即使輸入電壓保持不變,轉換結果也可能上下浮動。如果再加上一些電磁干擾,比如附近有電機,這種噪音會更加明顯。如果一個程序需要檢測電位器旋轉的位置在中點的哪邊,并僅僅是簡單地比較轉換結果與128的大小關系,這種噪聲會導致嚴重后果,如紅色波形所示:

在閾值128附近,噪聲使轉換結果上下浮動,導致判斷出的狀態迅速跳變。用戶只是慢慢地把旋鈕轉過中間的位置,這顯然不是我們想要的結果。


這時候就需要滯回比較器出場了。滯回比較器的核心特性是,使輸出在0和1之間改變的輸入閾值在兩個方向上是不同的:當信號從低到高越過高閾值時,輸出變為1;當信號從高到低越過低閾值時,輸出變為0;如綠色波形所示(圖中是反相的)。于是,當輸入達到高閾值時,輸出變為1,此時只要噪音沒有大到使輸入回到低閾值,輸出將一直保持為1,濾除了噪聲。


我們寫一個程序,用LED來指示電位器旋鈕位置在中點的哪一側,并在串口上輸出每一次狀態改變,方便我們觀察。


#include

#include

#include

#include


void init();

void normal();

void hysteresis();


int main()

{

    init();

    while (1)

    {

        normal();

//         hysteresis();

        delay(1);

    }

}


static bool status;


void change(bool _value)

{

    status = _value;

    uart_print_string(_value ? "onn" : "offn");

    led_set(LED_BLUE, _value);

}


void init()

{

    pot_init(ADC_0);

    led_init();

    uart_init(UART_TX);

    status = pot_read() >= 128;

}


void normal()

{

    bool now = pot_read() >= 128;

    if (status != now)

        change(now);

}


void hysteresis()

{

    uint8_t pot = pot_read();

    if (status && pot < 124)

        change(0);

    else if (!status && pot >= 132)

        change(1);

}

normal和hysteresis函數二選一,其中后者使用了滯回比較的算法。


在normal模式下,把電位器調整到中點附近的一個位置,你會發現黃色的TX指示燈發了瘋一樣地閃,串口軟件顯示一長串的“on”和“off”(仔細調,一定會有)——你根本不需要制造任何干擾,僅憑ADC的誤差就可以讓程序運行地非常糟糕。如果用滿10位的分辨率,這樣的現象會更加明顯。


而在hysteresis模式下,這樣的狀況不會出現。


光敏電阻

光敏電阻是一種特殊的電阻器,在光強的時候電阻小,在光弱的時候電阻大。將一個光敏電阻與一個普通電阻串聯,接在VCC和GND之間,測量中間點的電壓,就能知道光的強弱。


當然,已知開發板上與光敏電阻串聯的電阻是10kΩ,根據某一時刻的ADC轉換結果,也可以計算出此時光敏電阻的阻值。不過不要誤會,是通過電壓而不是阻值來獲得光強。


與電位器一樣,如果要檢測光的強與弱兩種狀態,也要用到滯回比較。取兩個閾值為100和150,兩者相差較大,這是因為我們要在光較弱時開燈,這又會增強亮度(有點負反饋的意味),如果相差不夠大,就會陷入循環當中。


這兩個閾值是隨便取的,實際應用應根據具體環境取值。于是容易想到要把這個功能從應用程序中抽離出來成為一個庫。但是,不同于之前常用的、返回外設狀態讓客戶來決定操作的函數(盡管還是可以這么寫),這個庫是事件驅動的:客戶注冊事件發生時要執行的動作,把程序流程交給框架來控制。


程序分為三個文件:event.h、event.c和main.c,前兩個可以獨立成庫,供以后使用,為了方便,和可執行程序放在一起了。


event.h:


#ifndef EVENT_H

#define EVENT_H


#include

#include


void ldr_event_init(uint8_t _thl, uint8_t _thh, void (*_func)(bool));

void ldr_event_cycle();


#endif

event.c:


#include "event.h"

#include


static void (*handler)(bool);

static uint8_t low, high;

static bool status;


void ldr_event_init(uint8_t _thl, uint8_t _thh, void (*_func)(bool))

{

    ldr_init(ADC_1);

    low = _thl;

    high = _thh;

    handler = _func;

    uint8_t ldr = ldr_read();

    if (ldr <= low)

        handler(status = 0);

    else

        handler(status = 1);

}


void ldr_event_cycle()

{

    uint8_t ldr = ldr_read();

    if (status && ldr <= low)

        handler(status = 0);

    else if (!status && ldr >= high)

        handler(status = 1);

}

main.c:


#include

#include

#include "event.h"


void handler(bool e)

{

    if (e)

        led_off();

    else

        led_on();

}


int main()

{

    led_init();

    ldr_event_init(100, 150, handler);

    while (1)

    {

        ldr_event_cycle();

        delay(1000);

    }

}


客戶先編寫事件處理函數handler,參數為一個bool,返回void,這是ldr_event_init所規定的。handler根據參數執行相應動作:當e為true時,光由弱變強,關燈;反之開燈。在調用ldr_event_init時,把這個函數的指針作為參數傳入。隨后,每隔1秒調用一次ldr_event_cycle。


請先花一點時間,把庫的每一行理解清楚。然后,我們站在客戶的角度來看,使用這個庫是相對方便的——只需考慮事件,即光的變化,而無需考慮過程,即如何檢測這一變化——事實上客戶根本沒有去檢測,更別說如何了。不過,main函數必須每隔一段時間調用一次ldr_event_cycle。在學了定時器中斷以后,main函數就可以完全還給客戶了。


關鍵字:AVR  單片機  ADC 引用地址:AVR單片機教程——ADC

上一篇:BMP180測量海拔高度傳感器單片機程序
下一篇:ATmega328P定時器詳解

推薦閱讀

作者:貝能國際有限公司Infineon公司CoolMOS?系列一直是行業標桿, 從跨時代意義的CoolMOS? C3到升級版的 CoolMOS? C6、 CoolMOS? C7、CoolMOS? P6、 CoolMOS?CE ,及最新一代超高性價比的CoolMOS? P7,每系列產品一經推出都在CoolMOS?市場獨領風騷。全新CoolMOS? P7專為滿足小功率SMPS市場的需求而設計,具備出色的性能和易用性,而...
#include "user_usart.h" #include "stm32f10x_usart.h" /*使用microLib的方法*/ /* int fputc(int ch, FILE *f){ USART_SendData(USART1, (uint8_t) ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch;}int GetKey (void) { while (!(USART1->SR & USART_FLAG_RXNE)); retur...
華為不會主動放棄歐洲市場,此前信息顯示,華為 Mate 40/Pro 系列新機已獲得了 EEC(歐亞經濟委員會)認證,預示華為將照常在歐洲地區推出該系列機型。另外還有Mate 40 Lite,也就是已經發布的華為麥芒9。現在華為Mate 40 Lite、華為Mate 40 Pro、華為Mate 40系列手機殼曝光,其中后兩款機型后置圓形“奧利奧”碩大相機模組。  另外...
基于電化學材料的基礎創新和產業化突破,正在讓中國企業占領全球動力電池技術“新高地”。8月29日,第二十四屆成都國際汽車展覽會在中國西部國際博覽城開幕,蜂巢能源是唯一一家參展的電池企業,在車展現場,搭載蜂巢能源無鈷電池的首款車型歐拉櫻桃貓也公開亮相。作為長城歐拉品牌旗下首款純電SUV,櫻桃貓車型最受關注的一大亮點,是其首次搭載了蜂巢能源...

史海拾趣

問答坊 | AI 解惑

數字技術中的模擬電路技術

[code language="J#"] [/code]    企業新聞 => 數字技術中的模擬電路技術 【 字體:大 中 小 】【打印此頁】 發布日期:[2003-01-09]    共閱[83]次      ...…

查看全部問答∨

繼續求教:PIC16位機哪種更適合

先設定應用背景: A測控16個溫度點,B控制4臺步進電機,C控制8個繼電器,D至少預留12個以上的測控端接受多種傳感器的電信號 還有一個小前提,芯片容易買到。因為我研究測控模塊只是為了提高產品的技術含量,不可能大批制造板子,更不會轉行 1、F ...…

查看全部問答∨

數碼相機的CMOS傳感器知識介紹

本帖最后由 jameswangsynnex 于 2015-3-3 20:02 編輯 CMOS即互補性金屬氧化物半導體。其在微處理器、閃存和特定用途集成電路(ASIC)的半導體技術上占有絕對重要的地位。CMOS和CCD都是可用來感受光線變化的半導體。CMOS主要是利用“硅”和“鍺”這兩 ...…

查看全部問答∨

vxworks環境下如何支持互斥 請大俠們看看

        pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); 編譯時報告PTHREAD_MUTEX_RECURSIVE沒有聲明, 如何改正?? …

查看全部問答∨

二層交換機的實現

本章節是我寫的 《嵌入式通信設備開發---基于MPC82xx 》一章的縮略版 請大家給看看 寫得如何 可以聯系我獲取其他章節 給出意見的都有分 文檔密碼:ads …

查看全部問答∨

求一個簡單的能產生正弦波信號的芯片

我的要求很簡單,就是需要10HZ信號,5V左右的穩定幅度就可以了。供電電源為正負5V。 查過很多(如XR2206,ICL8038,MAX038,XR8038)都停產了,有沒有還沒有停產的推薦一下呀,最好成本不是太高的那種。 先謝謝了。…

查看全部問答∨

編譯2.6.14.1的內核出錯了,有會的看看,已經一個星期了,急,在線..........

我用的是arm-linux-gcc3.3.2,在編譯kernel2.6.14.1時,報錯 /usr/local/arm/3.3.2/bin/arm-linux-ld:arch/arm/kernel/vmlinux.lds:711:parse error 這是什么錯誤啊,大概是什么原因引起的,如何修改呢? 大家幫幫忙,都弄了一個星期了,一點結果 ...…

查看全部問答∨

請問STUSB線路推薦的ST2052BD成本多少,有便宜的替代料嗎?

                                 請問ST USB線路推薦的ST2052BD成本多少,有便宜的替代料嗎?…

查看全部問答∨
小廣播
設計資源 培訓 開發板 精華推薦

最新單片機文章
何立民專欄 單片機及嵌入式寶典

北京航空航天大學教授,20余年來致力于單片機與嵌入式系統推廣工作。

 
EEWorld訂閱號

 
EEWorld服務號

 
汽車開發圈

 
機器人開發圈

電子工程世界版權所有 京ICP證060456號 京ICP備10001474號-1 電信業務審批[2006]字第258號函 京公網安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 潼南县| 遵义市| 洛隆县| 和林格尔县| 平定县| 江口县| 晋中市| 德清县| 吴堡县| 莫力| 武穴市| 喀喇| 黄大仙区| 无锡市| 龙海市| 孟津县| 惠东县| 布拖县| 甘谷县| 肥乡县| 松江区| 滁州市| 鹰潭市| 泗阳县| 建阳市| 肥乡县| 扶风县| 万山特区| 佛学| 双鸭山市| 江源县| 万全县| 西充县| 合山市| 贵溪市| 清原| 富顺县| 横峰县| 哈尔滨市| 尼勒克县| 青神县|