目录
一、前言
二、ADC外设简要说明
三、STM32CubeMX配置(本文使用的STM32CubeMX版本为6.1.2)
1.MCU选型
2.时钟使能
3.外部时钟配置
4.串口配置
5.ADC引脚配置
6.配置STM32CubeMX生成工程文件
7.点击GENERATE CODE生成工程文件
四、工程源码
五、运行状态
一、前言
本文主要介绍通过HAL库搭建工程及如何通过STM32L051的ADC外设读取外部电压;
1.MCU:STM32L051C8T6
2.软件平台:KEIL V5.27、STM32CubeMX V6.1.2
3.库类型:HAL
二、ADC外设简要说明
1.STM32L051这款MCU不像F1系列大容量版本的MCU有外置Vref引脚,所以在使用这款单片机时,如果采用外部的3.3V电源电压作为基准,那么在外部电压发生波动时就会影响ADC测量数据的准确性。这里有两种方法:一种是可采用在外部使用如TL431搭建基准电源电路,需要使用2路ADC通道,在ADC进行读取时先使用一路通道进行TL431基准电压的读取进行比较,然后在读取另一外部输入的通道;第二种是采用MCU内部的基准电压作为基准,在ADC进行读取时先获取内部的基准电压值,然后在读取另一外部输入的通道,这里例程采用的是第二种方法。
2.查看datasheet得知,STM32的mcu有一个寄存器VREFINT_CAL的值为厂家存放的基准电压,该值的环境:在温度为25°C时获取的原始VDD = 3V,由下图说明VREFINT内部基准电源为ADC和比较器提供稳定的电压输出。VREFINT内部连接ADC_IN17输入通道,它可以准确的监测VDD值。
3.查看STM32L051参考手册可找到厂家已给出相应的计算公式,如下图:
我们可以看到,手册中已说明,对于不知道VDDA值的应用,必须使用内部参考电压,内部参考电压Vdda=3V,ADC读取电压的计算公式也列出了。那么我们在使用ADC时就需要读取两个通道的值,首先为ADC_IN17的值(即VREFINT_DATA),ADC_DATA是外部ADC引脚通道上测量的值,VREFINT_CAL为内部参考电压校准值,可以直接地址读取。我们使用的这款STM32L051C8T6芯片VREFINT_CAL地址为:0X1FF80078。使用公式直接读取VREFINT_CAL = *(__IO uint16_t *)(0X1FF80078);FULL_SCALE是根据我们设置的ADC分辨率而定,12位ADC分辨率值:2^12 - 1 = 4096 - 1。
三、STM32CubeMX配置(本文使用的STM32CubeMX版本为6.1.2)
思路:使用PA0引脚(ADC_IN0通道)读取外部电压(通过电位器调节),读取后将读出的数值通过串口一打印出来。
1.MCU选型
例程使用的为STM32L051C8T6型号;
2.时钟使能
使用外部时钟
3.外部时钟配置
将时钟选用外部HSE时钟,频率采用32M
4.串口配置
启用USART1,模式为异步通讯模式。波特率采用9600,数据长度8为,无校验,1个停止位。
5.ADC引脚配置
选择使用PA0引脚作为ADC输入端,
ADC参数配置
6.配置STM32CubeMX生成工程文件
1.编辑好文件名称,保存路径,输出工程文件类型。
7.点击GENERATE CODE生成工程文件
四、工程源码
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t i;
uint32_t SUM[3];
uint16_t ADC_DATA[101];
uint16_t VREFINT_DATA[101];
ADC_ChannelConfTypeDef sConfig;
uint16_t VREFCAL = 0;
float V_CHANNEL = 0;
int16_t ADC_value;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/*获取内部校准电压参考值*/
VREFCAL = *(__IO uint16_t *)(0x1FF80078);
printf("\r\nSTM32L051C8T6 开发板ADC读取实验\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
SUM[0] = 0;
SUM[1] = 0;
for(i=0; i<100; i++)
{
/*获取PA0引脚电压值*/
ADC_DATA[i] = 0;
/*开启ADC校准,使用的为单端校准模式*/
HAL_ADCEx_Calibration_Start(&hadc,ADC_SINGLE_ENDED);
/*寄存器数据清零*/
hadc.Instance->CHSELR=0;
/*转换通道配置*/
sConfig.Channel = ADC_CHANNEL_0;
/*配置所选通道*/
HAL_ADC_ConfigChannel(&hadc, &sConfig);
/*启动转换*/
HAL_ADC_Start(&hadc);
/*等待转换结束,1000为times*/
HAL_ADC_PollForConversion(&hadc,1000);
/*读取结果*/
ADC_DATA[i] = HAL_ADC_GetValue(&hadc);
/*停止ADC读取*/
HAL_ADC_Stop(&hadc);
SUM[0] += ADC_DATA[i];
/*获取内部基准电压值*/
VREFINT_DATA[i] = 0;
HAL_ADCEx_Calibration_Start(&hadc,ADC_SINGLE_ENDED);
/*寄存器数据清零*/
hadc.Instance->CHSELR=0;
/*转换通道配置*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
/*配置所选通道*/
HAL_ADC_ConfigChannel(&hadc, &sConfig);
/*启动转换*/
HAL_ADC_Start(&hadc);
/*等待转换结束*/
HAL_ADC_PollForConversion(&hadc,1000);
/*读取结果*/
VREFINT_DATA[i] = HAL_ADC_GetValue(&hadc);
/*停止ADC读取*/
HAL_ADC_Stop(&hadc);
SUM[1] += VREFINT_DATA[i];
}
/*计算外部输入电压平均值*/
ADC_DATA[100] = SUM[0] / 100;
/*计算内部基准电压平均值*/
VREFINT_DATA[100] = SUM[1] / 100;
/*计算公式V_channelx = 3V *VREFINT_CAL*ADC_DATAx/(VREFINT_DATA*FULL_SCALE)*/
/*V_channel为计算后的值,VREFCAL为内部基准值、FULL_SCALE为12位分辨率即4096-1*/
/*ADC_DATA为外部采集电压值,VREFINT_DATA为通道17采集的值*/
/*计算校准后的电压值*/
V_CHANNEL = (float)(3*VREFCAL*ADC_DATA[100])/(VREFINT_DATA[100]*4095);
/*电压值放大1000倍*/
ADC_value = V_CHANNEL * 1000;
/*通过打印ADC_value值*/
printf("ADC数据:%dmv",ADC_value);
/*延时*/
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
五、运行状态
运行状态如下,ADC读取数据还是蛮准的。
万用表读取数据,ADC读取数据与万用表基本差不多。
工程源码链接:STM32L051C8T6_ADC读取例程源码