ADC-DAC


一,ADC

模擬信號

什么是模擬信號?主要是離散的數字信號相對的連續的信號。模擬信號分布於自然界的各個角落,如每天溫度的變化,

數字信號是人為的抽象出來的在時間上不連續的信號。電學上的模擬信號是主要是指幅度和相位都連續的電信號,

此信號可以被模擬電路進行各種運算,如放大,相加,相乘等。模擬信號是指用連續變化的物理量表示的信息

其信號的幅度,或頻率,或相位隨時間作連續變化,如目前廣播的聲音信號,或圖像信號等 。常見的模擬信號有正弦波、 調幅波、 阻尼震盪波、 指數衰減波

 

                      

 

數字信號

什么是數字信號?

數字信號指幅度的取值是離散的,幅值表示被限制在有限個數值之內。 二進制碼就是一種數字信號

二進制碼受噪聲的影響小,易於有數字電路進行處理,所以得到了廣泛的應用

              

優點:

抗干擾能力強、無噪聲積累

在模擬通信中,為了提高信噪比,需要在信號傳輸過程中及時對衰減的傳輸信號進行放大信號在傳輸過程中不可避免地疊加上的噪聲也被同時放大

隨着傳輸距離的增加,噪聲累積越來越多,以致使傳輸質量嚴重惡化。

對於數字通信,由於數字信號的幅值為有限個離散值(通常取兩個幅值),在傳輸過程中雖然也受到噪聲的干擾,但當信噪比惡化到一定程度時,

即在適當的距離采用判決再生的方法,再生成沒有噪聲干擾的和原發送端一樣的數字信號,所以可實現長距離高質量的傳輸。

便於加密處理

信息傳輸的安全性和保密性越來越重要,數字通信的加密處理的比模擬通信容易得多,以話音信號為例,經過數字變換后的信號可用簡單的數字邏輯運算進行加密、解密處理。

便於存儲、處理和交換

數字通信的信號形式和計算機所用信號一致,都是二進制代碼,因此便於與計算機聯網,也便於用計算機對數字信號進行存儲、處理和交換,

可使通信網的管理、維護實現自動化、智能化。

設備便於集成化、微型

數字通信采用時分多路復用,不需要體積較大的濾波器。設備中大部分電路是數字電路,可用大規模和超大規模集成電路實現,因此體積小、功耗低。

便於構成綜合數字網和綜合業務數字網

采用數字傳輸方式,可以通過程控數字交換設備進行數字交換,以實現傳輸和交換的綜合。

另外,電話業務和各種非話業務都可以實現數字化,構成綜合業務數字網。

占用信道頻帶較寬

一路模擬電話的頻帶為4kHz帶寬,一路數字電話約占64kHz,這是模擬通信目前仍有生命力的主要原因。

隨着寬頻帶信道(光纜、數字微波)的大量利用(一對光纜可開通幾千路電話)以及數字信號處理技術的發展(可將一路數字電話的數碼率由64kb/s壓縮到32kb/s甚至更低的數碼率),

數字電話的帶寬問題已不是主要問題了

 

常用的數字信號編碼有不歸零(NRZ)編碼、 曼徹斯特(Manchester)編碼和差分曼徹斯特(Differential Manchester)編碼。

                

 

數字信號與模擬信號的轉化

模擬信號和數字信號之間可以相互轉換:模擬信號一般通過PCM脈碼調制(Pulse Code Modulation)方法量化為數字信號

即讓模擬信號的不同幅度分別對應不同的二進制值,例如采用8位編碼可將模擬信號量化為2^8=256個量級,實用中常采取24位或30位編碼;

數字信號一般通過對載波進行移相(Phase Shift)的方法轉換為模擬信號。計算機、計算機局域網與城域網中均使用二進制數字信號,

目前在計算機廣域網中實際傳送的則既有二進制數字信號,也有由數字信號轉換而得的模擬信號。但是更具應用發展前景的是數字信號。

 

PCM脈碼調制

脈沖編碼調制就是把一個時間連續,取值連續的模擬信號變換成時間離散,取值離散的數字信號后在信道中傳輸。

脈沖編碼調制就是對模擬信號先抽樣,再對樣值幅度量化, 編碼的過程

 

抽樣: 就是對模擬信號進行周期性掃描,把時間上連續的信號變成時間上離散的信號。

該模擬信號經過抽樣后還應當包含原信號中所有信息,也就是說能無失真的恢復原模擬信號。

量化: 就是把經過抽樣得到的瞬時值將其幅度離散,即用一組規定的電平,把瞬時抽樣值用最接近的電平值來表示,通常是用二進制表示。

編碼: 就是用一組二進制碼組來表示每一個有固定電平的量化值。然而,實際上量化是在編碼過程中同時完成的,故編碼過程也稱為模/數變換,可記作A/D。

 

STM32F4手冊

 

 

如何知道當前是使用哪個ADC硬件,同時使用哪個通道?

PA5/ADC12_IN5,表示PA5引腳支持ADC1或ADC2進行掃描,使用通道是第五個輸入通道

存儲對齊方式

 

通過ADC硬件獲取到的結果值,為什么還得要進行轉換為電壓值,依據是什么?

 

通過調整可調電阻實現燈光亮度的變化

 

int main(void)
{
    uint32_t adc_val,adc_vol,pwm_cmp=0;;

    //系統時鍾的時鍾源=168MHz/8=21MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

    //串口初始化,波特率為115200bps
    usart1_init(115200);

    //adc初始化,12位精度
    adc_init();

    //定時器14初始化為PWM,使用PWM通道1,當前頻率為100Hz
    tim14_init();

    while(1)
    {
        /* Start ADC Software Conversion ,啟動ADC*/ 
        ADC_SoftwareStartConv(ADC1);    

        //等待轉換結束
        while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);

        //獲取ADC轉換后的數值
        adc_val=ADC_GetConversionValue(ADC1);

        //將ADC的數值轉換為電壓值
        adc_vol = adc_val *3300/4095;


        printf("vol=%dmv\r\n",adc_vol);

        //設置比較值
        pwm_cmp = adc_vol*100/3300 ;

        TIM_SetCompare1(TIM14,pwm_cmp);

        printf("pwm compare=%d\r\n",pwm_cmp);

        delay_ms(500);
        
    }
}

 

void tim14_init(void)
{
    /* GPIOF clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

    /* GPIOF Configuration: TIM14 CH1 (PF9) */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;            //復用功能,使用引腳的第二功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOF, &GPIO_InitStructure); 

    /* Connect TIM pins to AF9 */
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14);

    /* TIM14 clock enable */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);


    /* Time base configuration,100Hz*/
    TIM_TimeBaseStructure.TIM_Period = (10000/100)-1;                    //定時計數值,100Hz
    TIM_TimeBaseStructure.TIM_Prescaler = 8400;                            //預分頻值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                //再次進行1分頻
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;            //向上計數
    TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);


    /* PWM1 Mode configuration: Channel1 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM14, &TIM_OCInitStructure);


    TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);                    //自動重載初值,不斷輸出PWM脈沖
    TIM_ARRPreloadConfig(TIM14, ENABLE);                                //自動重載使能                        

    /* TIM14 enable counter */
    TIM_Cmd(TIM14, ENABLE);

}
tim14_init

 

void adc_init(void)
{
    //使能GPIOA的硬件時鍾
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  
    
    //使能ADC1硬件時鍾
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    /* 配置ADC1通道5為模擬輸入引腳 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                                        //第5號引腳
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;                                    //引腳設置為模擬輸入,能夠識別更加廣范圍的電平(0V~3.3V任何電壓)
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* ADC常規的初始化 */
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                        //獨立模式,在當前的通道5只采用1個ADC硬件進行工作
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;                        //ADC硬件的工作時鍾= APB2(84MHz)/2=42MHz
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;            //禁止DMA
    //ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;    //如果采用多個ADC對某一個通道進行采樣的時候,才需要設置
    ADC_CommonInit(&ADC_CommonInitStructure);
    
    
    /* ADC1初始化*/
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;                            //12位精度,非常重要
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;                                    //因不需要DMA,則不需要使用自動掃描模式。當前使用軟件觸發一次,則掃描一次                                
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                //模擬數字轉換器一直工作
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;        //禁止觸發檢測,不需要外部引腳電平識別來讓ADC硬件工作
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                            //右對齊
    ADC_InitStructure.ADC_NbrOfConversion = 1;                                        //執行一次轉換結果
    ADC_Init(ADC1, &ADC_InitStructure);
    
    /* 指定ADC1常規通道5的采樣時間,采樣時間 = 3個ADC時鍾時間 = 3* (1/42MHz)*/
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_3Cycles);

    /* Enable ADC1,使能ADC1 */
    ADC_Cmd(ADC1, ENABLE);

}
adc_init

 

光敏電阻

 原理

光敏電阻的工作原理:光照時,電阻很小無光照時,電阻很大。光照越強,電阻越小;光照停止,電阻又恢復原值。

應用案例:手機自動亮度,根據的光照的強度來動態調整手機屏幕的亮度。

 

二,DAC

 

 

 

 

#include <stdio.h>
#include "stm32f4xx.h"
#include "sys.h"

static GPIO_InitTypeDef   GPIO_InitStructure;

static NVIC_InitTypeDef   NVIC_InitStructure;

static USART_InitTypeDef  USART_InitStructure;

static ADC_InitTypeDef       ADC_InitStructure;
static ADC_CommonInitTypeDef ADC_CommonInitStructure;

static DAC_InitTypeDef      DAC_InitStructure;


//重定義fputc函數 
int fputc(int ch, FILE *f)
{     
    USART_SendData(USART1,ch);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  
    
    return ch;
}   

void delay_us(uint32_t nus)
{        
    uint32_t temp;             
    SysTick->LOAD =SystemCoreClock/8/1000000*nus;     //時間加載               
    SysTick->VAL  =0x00;                            //清空計數器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;         //使能滴答定時器開始倒數      
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待時間到達   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;         //關閉計數器
    SysTick->VAL =0X00;                               //清空計數器 
}

void delay_ms(uint16_t nms)
{                     
    uint32_t temp;           
    SysTick->LOAD=SystemCoreClock/8/1000*nms;        //時間加載(SysTick->LOAD為24bit)
    SysTick->VAL =0x00;                               //清空計數器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //能滴答定時器開始倒數 
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));            //等待時間到達   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //關閉計數器
    SysTick->VAL =0X00;                               //清空計數器              
} 

void USART1_Init(uint32_t baud)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);                             //使能GPIOA時鍾
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);                            //使能USART1時鍾
 
    //串口1對應引腳復用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);                         //GPIOA9復用為USART1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);                         //GPIOA10復用為USART1
    
    //USART1端口配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;                         //GPIOA9與GPIOA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                    //復用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                     //推挽復用輸出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                                     //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure);                                             //初始化PA9,PA10

    //USART1 初始化設置
    USART_InitStructure.USART_BaudRate = baud;                                        //波特率設置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                        //字長為8位數據格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                            //一個停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;                                //無奇偶校驗位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    //無硬件數據流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                    //收發模式
    USART_Init(USART1, &USART_InitStructure);                                         //初始化串口
    
    USART_Cmd(USART1, ENABLE);                                                      //使能串口1 
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                    //開啟相關中斷

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;                                //串口1中斷通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;                            //搶占優先級3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;                                //子優先級3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                    //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                                                    //根據指定的參數初始化VIC寄存器
}



void adc_init(void)
{

    /* Enable ADC,and GPIO clocks ,使能ADC與GPIO的時鍾*/ 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    /* Configure ADC1 Channel3 pin as analog input ,配置ADC1的通道3作為模擬輸入引腳*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;        //模擬輸入模式
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    
    /* ADC Common Init ,ADC的常規初始化*/
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                        //獨立模式,在當前的通道5只能使用ADC1進行掃描,不需要二重/三重掃描
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;                        //ADC硬件的工作時鍾=APB2(84MHz)/2=42MHz
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;            //禁止DMA
    //ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;    //如果采用二重/三重采樣的時候,才需要設置
    ADC_CommonInit(&ADC_CommonInitStructure);

    

    /* ADC1 Init ,ADC1初始化*/
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;                            //12位精度,非常重要[*]
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;                                    //因不需要多重采樣,則不需要連續掃描
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                //模擬數字轉換器一直工作
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;        //禁止外部觸發進行模擬轉換工作
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                            //存儲的數據以右對齊的方式進行存儲[*]
    ADC_InitStructure.ADC_NbrOfConversion = 1;                                        //執行一次轉換結果
    ADC_Init(ADC1, &ADC_InitStructure);


    /* ADC1 regular channel3 configuration,指定ADC1常規通道3的采樣時間,使用3個ADC時鍾時間*/
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_3Cycles);

    /* Enable ADC1,使能ADC1 */
    ADC_Cmd(ADC1, ENABLE);

}


void dac_init(void)
{

    /* DAC Periph clock enable ,DAC硬件時鍾使能*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
    
    //GPIOA的硬件時鍾使能
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  

    //設置PA4引腳為模擬信號引腳
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* DAC channel1 Configuration ,DAC通道1的初始化*/
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;                        //不依賴其他定時器來觸發DAC硬件的輸出,通過軟件觸發輸出電壓值
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;            //不需要輸出任何的波形,電壓值是由程序來決定
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;            //輸出使能
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);
    
    /* Enable DAC Channel2 使能DAC的通道1*/
    DAC_Cmd(DAC_Channel_1, ENABLE);
    
    /* Set DAC channel1 DHR12RD register,設置DAC的輸出值以12位的右對齊方式,也就是設置DAC輸出的電壓值, */
    DAC_SetChannel1Data(DAC_Align_12b_R, 0);

}


int main(void)
{
    uint32_t adc_vol=0;
    uint32_t adc_val=0;
    uint32_t dac_vol=0;
    
    //系統定時器初始化,時鍾源來自HCLK,且進行8分頻,
    //系統定時器時鍾頻率=168MHz/8=21MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
        
    //設置中斷優先級分組2
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    //串口1,波特率115200bps,開啟接收中斷
    USART1_Init(115200);

    //adc初始化
    adc_init();
    
    //dac初始化
    dac_init();
    
    printf("This is adc dac test\r\n");
    
    while(1)
    {
        dac_vol+=100;
        
        //使用DAC1輸出自定義的電壓值
        DAC_SetChannel1Data(DAC_Align_12b_R, dac_vol*4095/3300);
        
        printf("dac vol=%dmv\r\n",dac_vol);
        
        if(dac_vol >=3300)
            dac_vol=0;
        
        //延時
        delay_ms(500);        
        
        //啟動ADC1進行轉換
        ADC_SoftwareStartConv(ADC1);
        
        
        //等待轉換結束
        while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
        
        
        
        //獲取結果值
        adc_val=ADC_GetConversionValue(ADC1);
        
        //將結果值轉換為電壓值
        adc_vol = adc_val *3300/0xFFF;
        
        
        //將電壓值進行打印輸出
        printf("adc vol=%dmv\r\n",adc_vol);
        
        
        //延時
        delay_ms(500);

    }

}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM