有一个温度测控系统,已知温度传感器在 0℃ 到 100℃ 之间为线性输出,参考电压为 5V,采用 8 位的 A/D 转换器,0℃ 的时候测得电压为 1.8V,100℃ 的时候测得电压为 4.3V。
求:1、系统的分辨率。
2、若采集到数据 1001 0001B,表示的电压?表示的温度?
解:1、分辨率(电压)= 5V / 28 = 0.0195V = 1.95mV
分辨率(温度)= 0.0195 * 40 = 0.78℃,在采用电压 1.8V ~ 4.3V 之中,采样电压每上升 0.0195V,所代表的温度就上升 0.78℃。
2、温度 = 40 * ( 采样得到电压 - 1.8 )
1001 0001B = 91H = 145
表示的电压 = 145 * 0.0195V ≈ 2.83V
表示的温度 = 40 * ( 2.83 - 1.8 ) = 41.2℃
STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC 可以独立使用,也可以使用双重模式(提高采样率)。 STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个测量通道,可测量 16 个外部和 2 个内部信号源(内部温度和内部参考电压)。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
STM32F103 系列最少都拥有 2 个 ADC。STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14Mhz(在 STM32CubeMX 中配置时钟树时应注意),否则将导致结果准确度下降。
STM32 将 ADC 的组织形式划分为 2 个通道组:规则通道组和注入通道组。规则通道组 ADC 可以对一组最多 16 个通道按照指定的顺序逐个进行转换,相当于正常运行的程序;在实际应用中,可能需要中断规则组的转换,临时对某些通道进行转换,称为注入通道组,最多由 4 个通道组成,就相当于中断,在你程序正常执行的时候,打断你的执行。注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
A/D 转换结果有 2 种存储方式:左对齐、右对齐。
STM32F103RCT6 的 ADC1 通道 0 在 PA0 上。
① 查询方式,阻塞式 A/D 转换:
uint_t ADC_Value = 0;
HAL_ADC_Start(&hadc1);
if(HAL_ADC_PollForConversion(&hadc1,10) == HAL_OK){
ADC_Value = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);
② 中断方式,非阻塞式 A/D 转换:
uint_t ADC_Value = 0;
HAL_ADC_Start_IT(&hadc1);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1){
ADC_Value = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop_IT(&hadc1);
}
}
示例:将ADC_IN0
设置为 12 位 ADC,右对齐,启用中断,分别用查询和中断 2 种方式,每隔 0.5 秒采样一次 ADC 的数据(使用定时器计时),将每次读取到的 ADC 采样值转换为对应的电压值,通过USART1
发送到上位机。
参考代码(查询方式):
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include "stdio.h"
#define LED0_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET)
#define LED0_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET)
uint16_t ADC_Value = 0;
float ADC_Volt = 0;
uint8_t Str_Buf[64] = {'\0'};
void ADC_UR1_Send()
{
sprintf((char*)Str_Buf, "采样值:%d,电压值:%fV。\r\n", ADC_Value, (ADC_Volt/1000));
HAL_UART_Transmit(&huart1, Str_Buf, sizeof(Str_Buf), 10000);
}
void ADC0_Get_Value()
{
HAL_ADC_Start(&hadc1);
if(HAL_ADC_PollForConversion(&hadc1,10) == HAL_OK){
ADC_Value = HAL_ADC_GetValue(&hadc1);
ADC_Volt = (ADC_Value * 3300 / 4096);
}
HAL_ADC_Stop(&hadc1);
ADC_UR1_Send();
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2){
ADC0_Get_Value();
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
参考代码(中断方式):
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include "stdio.h"
#define LED0_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET)
#define LED0_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET)
uint16_t ADC_Value = 0;
float ADC_Volt = 0;
uint8_t Str_Buf[64] = {'\0'};
void ADC_UR1_Send()
{
sprintf((char*)Str_Buf, "采样值:%d,电压值:%fV。\r\n", ADC_Value, (ADC_Volt/1000));
HAL_UART_Transmit(&huart1, Str_Buf, sizeof(Str_Buf), 10000);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2){
HAL_ADC_Start_IT(&hadc1);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1){
ADC_Value = HAL_ADC_GetValue(&hadc1);
ADC_Volt = (ADC_Value * 3300 / 4096);
HAL_ADC_Stop_IT(&hadc1);
ADC_UR1_Send();
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */