今天需要了解下A/D转换模块。大概就是ADC或者DAC。
一般来说除了老版的51单片机,现在的单片机大多都是自带AD转换的。主要是电压的变化,弄成数字变化,就可以进行编程了。
详细的说明就:
ADC:将时间和幅值连续的模拟量转化为时间和幅值离散的数字量,A/D转换一般要经过采样、保持、量化、编码4个步骤。
其中常用的一般类型就不再赘述了。反正也记不住。一般我们考虑的就是用到的时候在取看一下手册。
一般来讲有以下几个基本的技术指标:
量程:即它可以测量的电压范围,信号类型这种参考类的参数。信号包括了单极性和双极性。
转换位数:量化过程中的量化位数n(听不懂没啥,就是AD转换之后A/D用多少位来表示):比如10位AD就是0-1023;
分辨率:能分辨的最小变化量。分辨率=量程/2n;有些低功耗的物件,需要更小的电压变化量,这时候你就需要更高转换位数的AD了。
转换时间:一次AD转换的全过程所需要的时间。
那么接下来讲讲AD转换的基本过程:
采样:把连续的模拟信号,编程一种数字信号,就是一根一根的。这个东西我记得《信号与系统》或者《自动化原理》里都有讲,所以需要大家去学习一下。
之后就开始保持信号状态,然后进行量化和编码。就慢慢从模拟信号编程数字信号了。
咋解释呢,就是我通过傅里叶拉氏变换这些变化之后,现在我的信号已经变成离散的了。我需要一点一点来得出每一部分的一个数字信号,这个过程就需要我的信号在一段时间内保持住才可以。
当然这个过程,可以理解为啥函数每一个点来求微分的一个过程,只不过用滤波的思维去思考感觉会好一点。
然后整个过程是存在量化和编码误差的,这无法避免。不过分辨率越高,整个过程就误差越小。
一般来说,ADC的一个采样可以通过反馈的数据,来得到目前的电压值。我们可以简单讲一讲。因为我记得这个是我本科EDA需要学的东西.系统里可以通过现有的程序,已经给你算好了。不过同样的可以理解下,加深印象。
比如,一个温度传感器模块,范围为0-100度,参考电压是5V,AD转换器是8位的,0度是测得温度1.8V,100度时测的温度时4.3V,这时候我们需要求他的分辨率以及采集数据比如说时10010001的时候,多大的电压和温度?
电压分辨率:5 X 1/28=0.0195V;
温度分辨率:0.0195 X 40=0.78℃;
假设变化时Y=kX+B这样的线性变化;
求一下斜率k=(100-0)/(4.3-1.8)=40;
所以说就可以求的Y=40 X (x - 1.8);
所以说最后求的温度时(二进制)10010001=(十进制)145;
所以:0.0195 X 145=2.83V;
所以:(2.83-1.8)X 40=41.2℃;
STM32的ADC资源
(以下来自其他的资料,简单阐述下)一般来说,这个我们可以看芯片手册上的东西。比如STM32F103上面就有ADC1/2/3的共三个12位逐次逼近型模数转换器。具有18个测量通道。可以测量16个外部和2个内部的信号。其中呢,只有ADC1可以测量这两个信号源。
各个ADC通道中,A/D转换可以单次、连续、扫描、间断模式进行执行。
按照A/D转换的组织形式来说,ADC的模拟输入通道分为规则组和注入组:
规则组:ADC可以对一组最多16个通道按照指定的一个顺序进行逐个转换,这组指定通道称为规则组。
注入组:如果说需要中断规则组的转换,临时对某些通道进行转换,好像这些通道注入了原来的规则组,就称为注入组。一般来说,没咋用。最多有4个。
那么接下来,我们来配置一下。
ADC:一般来说12位,右对齐,然后使能个中断就行了。
然后需要了解下AD的一个查询方式。
一般来说ADC和USART一样,存在阻塞式的查询方式和非阻塞式的查询方式。
主要来看看这两种函数的结构:
阻塞式:
Uint_16 ADC_Value = 0;
HAL_ADC_Start(&hadc);
If(HAL_OK == HAL_ADC_PollForConversion(&hadc,10))
{
ADC0_Value = HAL_ADC_GetValue(&hadc);
}
非阻塞式:
Uint_16 ADC_Value(&hadc);
Void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc1)
{
ADC0_Value = HAL_ADC_GetValue(&hadc);
}
那么接下来,同样的做一个简单的例子。用到采样来实现功能。
首先ADC设置为12位,右对齐,中断使能
分别使用查询和中断,每0.5S检测ADC数据
每次读取的ADC采样值转换为电压值发送到上位机
LED1作为采样的指示灯,在ADC转换中点亮,其余时间熄灭。
那么接下来,根据题目,我觉得需要配置一个0.5s的定时器,一个LED的输出,ADC以及串口发送数据。那么大致我看来就是这几个操作。
接下来,我把核心的代码发在下面,大家作为一个参考。
#include "main.h" #include "adc.h" #include "usart.h" #include "gpio.h" #include "stdio.h" void SystemClock_Config(void); #define LED1_ON() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_RESET);//两个宏定义,用于定义LED的开关亮灭 #define LED1_OFF() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET); uint16_t ADC_Value =0,ADC_Volt =0;//这是初始化的两个值,分别用于计算采样值和电压值 uint8_t str_buff[64];//用于字符串的格式化 void UR1_Send_Info() { sprintf((char*)str_buff,"采样值: %d,电压值: %d.%d%dV\r\n",ADC_Value,ADC_Volt/100,(ADC_Volt%100)/10,ADC_Volt%10); HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000); } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){ if(hadc->Instance == ADC1) { ADC_Value = HAL_ADC_GetValue(&hadc1); ADC_Volt =ADC_Value*330/4096; UR1_Send_Info(); LED1_OFF(); } } /*下面我进行注释的时通过查询的方式进行的采样*/ /*void ADC0_GET_VALUE() { HAL_ADC_Start(&hadc1);//初始化 LED1_ON();//初始化后点灯 if(HAL_ADC_PollForConversion(&hadc1,10)==HAL_OK)//如果ADC转换查询正常 { ADC_Value = HAL_ADC_GetValue(&hadc1);//那么就读取ADC此时的值 ADC_Volt = ADC_Value * 3300/4096;//计算电压值,3300就是3300mV,4096就是2的12次方 } UR1_Send_Info(); LED1_OFF(); HAL_ADC_Stop(&hadc1); }*/ /** * @brief The application entry point. * @retval int */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); while (1) { /* USER CODE END WHILE */ /*ADC0_GET_VALUE(); HAL_Delay(500);*/ LED1_ON(); HAL_ADC_Start_IT(&hadc1); HAL_Delay(500); } }
今天在实验室加班到了11:00,现在还没下班,点个赞吧