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); }
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); }
光敏电阻
原理
光敏电阻的工作原理:光照时,电阻很小;无光照时,电阻很大。光照越强,电阻越小;光照停止,电阻又恢复原值。
应用案例:手机自动亮度,根据的光照的强度来动态调整手机屏幕的亮度。
二,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); } }