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

 

posted @ 2019-06-01 08:46  狂奔~  阅读(2593)  评论(0编辑  收藏  举报