完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、ADC简介
ADC(Analog-to-Digital Converter),即模拟-数字转换器,可以将连续变化的模拟信号转换为离散的数字信号,进而使用数字电路进行处理,称之为数字信号处理。二、ADC通道选择 STM32 的 ADC 多达 18 个通道,其中外部的 16 个通道就是框图中的 ADCx_IN0、ADCx_IN1…ADCx_IN5。这 16 个通道对应着不同的 IO 口,具体是哪一个 IO 口可以从手册查询到。其中 ADC1/2/3 还有内部通道:ADC1 的通道 16 连接到了芯片内部的温度传感器,Vrefint 连接到了通道 17。ADC2 的模拟通道 16 和 17 连接到了内部的 VSS。ADC3 的模拟通道 9、14、15、16 和 17 连接到了内部的 VSS。 三、引脚确定 开发板板载一个贴片滑动变阻器,引脚为 PC1,对应 ADC1 的通道 11 四、新建工程 1. 打开 STM32CubeMX 软件,点击“新建工程” 2. 选择 MCU 和封装 3. 配置时钟 RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器) 选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz 修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置 4. 配置调试模式 非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器 SYS 设置,选择 Debug 为 Serial Wire 五、ADC1 5.1 参数配置 在 Analog 中选择 ADC1 设置,并选择 IN11 通道11 或者在右边图找到 PC1 引脚,选择 ADC1_IN11 具体配置参数如下。
使能 ADC 中断 5.3 ADC时钟配置 ADC 的转换时间跟 ADC 的输入时钟和采样时间有关。 公式为:Tconv = 采样时间 + 12.5 个周期。当 ADCLK = 14MHZ (最高),采样时间设置为 1.5 周期(最快),那么总的转换时间(最短)Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。 一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us,这个才是最常用的。 5.4 生成代码 输入项目名和项目路径 选择应用的 IDE 开发环境 MDK-ARM V5 每个外设生成独立的 ’.c/.h’ 文件 不勾:所有初始化代码都生成在 main.c 勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。 点击 GENERATE CODE 生成代码 六、独立模式单通道采集中断方式 单通道采集适用 AD 转换完成中断,在中断服务函数中读取数据,不使用 DMA 传输,在多通道采集时才使用 DMA 传输。 6.1 修改中断回调函数 打开 stm32f1xx_it.c 中断服务函数文件,找到 ADC1 中断的服务函数 ADC1_2_IRQHandler() 中断服务函数里面就调用了 ADC 中断处理函数 HAL_ADC_IRQHandler() 打开 stm32f1xx_hal_adc.c 文件,找到 ADC 中断处理函数原型 HAL_ADC_IRQHandler(),其主要作用就是判断是哪个 ADC 产生中断,清除中断标识位,然后调用中断回调函数 HAL_ADC_ConvCpltCallback()。 /* NOTE: This function Should not be modified, when the callback is needed,HAL_ADC_ConvCpltCallback() 按照官方提示我们应该再次定义该函数,__weak 是一个弱化标识,带有这个的函数就是一个弱化函数,就是你可以在其他地方写一个名称和参数都一模一样的函数,编译器就会忽略这一个函数,而去执行你写的那个函数;而 UNUSED(hadc) ,这就是一个防报错的定义,当传进来的ADC号没有做任何处理的时候,编译器也不会报出警告。其实我们在开发的时候已经不需要去理会中断服务函数了,只需要找到这个中断回调函数并将其重写即可而这个回调函数还有一点非常便利的地方这里没有体现出来,就是当同时有多个中断使能的时候,STM32CubeMX会自动地将几个中断的服务函数规整到一起并调用一个回调函数,也就是无论几个中断,我们只需要重写一个回调函并判断传进来的定时器号即可。 接下来我们就在 stm32f1xx_it.c 这个文件的最下面添加 HAL_ADC_ConvCpltCallback() /* USER CODE BEGIN EV */ extern __IO uint32_t ADC_ConvertedValue; /* USER CODE END EV */ /* USER CODE BEGIN 1 */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { ADC_ConvertedValue = HAL_ADC_GetValue(hadc); } /* USER CODE END 1 */ 在中断回调函数中进行读取数据,将数据存放在变量 ADC_ConvertedValue 中。 6.2 添加全局变量 在 main.c 定义相关变量。 // ADC转换值 __IO uint32_t ADC_ConvertedValue; // 用于保存转换计算后的电压值 float ADC_Vol; 6.3 添加ADC中断启动函数 在 main.c 中,while 循环前,ADC初始化后,添加ADC中断开启函数,这样在第一次接收到数据的时候才会触发中断。 /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* 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_USART1_UART_Init(); MX_ADC1_Init(); /* USER CODE BEGIN 2 */ HAL_ADCEx_Calibration_Start(&hadc1); //AD校准 HAL_ADC_Start_IT(&hadc1); //开启ADC中断转换 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 6.4 添加电压值转换 模拟电压经过 ADC 转换后,是一个 12 位的数字值,如果通过串口以 16 进制打印出来的话,可读性比较差,那么有时候我们就需要把数字电压转换成模拟电压,也可以跟实际的模拟电压(用万用表测)对比,看看转换是否准确。 我们一般在设计原理图的时候会把 ADC 的输入电压范围设定在:0~3.3v,因为 ADC 是 12 位的,那么 12 位满量程对应的就是 3.3V,12 位满量程对应的数字值是:2^12。数值 0 对应的就是 0V。如果转换后的数值为 X ,X 对应的模拟电压为 Y,那么会有这么一个等式成立: 2^12 / 3.3 = X / Y,=> Y = (3.3 * X ) / 2^12。 /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { ADC_Vol =(float) ADC_ConvertedValue/4096*3.3; // 读取转换的AD倿 printf("The current AD value = 0x%04X rn", ADC_ConvertedValue); printf("The current AD value = %f V rnrn",ADC_Vol); //实际电压倿 HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ 6.5 HAL库与标准库代码比较 STM32CubeMX 使用 HAL 库生成的代码: __IO uint32_t ADC_ConvertedValue; /** * @brief ADC1 Initialization Function * @param None * @retval None */ static void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_11; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ } /** * @brief This function handles ADC1 and ADC2 global interrupts. */ void ADC1_2_IRQHandler(void) { /* USER CODE BEGIN ADC1_2_IRQn 0 */ /* USER CODE END ADC1_2_IRQn 0 */ HAL_ADC_IRQHandler(&hadc1); /* USER CODE BEGIN ADC1_2_IRQn 1 */ /* USER CODE END ADC1_2_IRQn 1 */ } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { ADC_ConvertedValue = HAL_ADC_GetValue(hadc); } HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start_IT(&hadc1); 使用 STM32 标准库的代码: __IO uint16_t ADC_ConvertedValue; /** * @brief ADC GPIO 初始化 * @param 无 * @retval 无 */ static void ADCx_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 打开 ADC IO端口时钟 ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE ); // 配置 ADC IO 引脚模式 // 必须为模拟输入 GPIO_InitStructure.GPIO_Pin = ADC_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 初始化 ADC IO GPIO_Init(ADC_PORT, &GPIO_InitStructure); } /** * @brief 配置ADC工作模式 * @param 无 * @retval 无 */ static void ADCx_Mode_Config(void) { ADC_InitTypeDef ADC_InitStructure; // 打开ADC时钟 ADC_APBxClock_FUN ( ADC_CLK, ENABLE ); // ADC 模式配置 // 只使用一个ADC,属于独立模式 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 禁止扫描模式,多通道才要,单通道不需要 ADC_InitStructure.ADC_ScanConvMode = DISABLE ; // 连续转换模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 不用外部触发转换,软件开启即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 转换结果右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 转换通道1个 ADC_InitStructure.ADC_NbrOfChannel = 1; // 初始化ADC ADC_Init(ADCx, &ADC_InitStructure); // 配置ADC时钟为PCLK2的8分频,即9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置 ADC 通道转换顺序和采样时间 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5); // ADC 转换结束产生中断,在中断服务程序中读取转换值 ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE); // 开启ADC ,并开始转换 ADC_Cmd(ADCx, ENABLE); // 初始化ADC 校准寄存器 ADC_ResetCalibration(ADCx); // 等待校准寄存器初始化完成 while(ADC_GetResetCalibrationStatus(ADCx)); // ADC开始校准 ADC_StartCalibration(ADCx); // 等待校准完成 while(ADC_GetCalibrationStatus(ADCx)); // 由于没有采用外部触发,所以使用软件触发ADC转换 ADC_SoftwareStartConvCmd(ADCx, ENABLE); } static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /** * @brief ADC初始化 * @param 无 * @retval 无 */ void ADCx_Init(void) { ADCx_GPIO_Config(); ADCx_Mode_Config(); ADC_NVIC_Config(); } void ADC_IRQHandler(void) { if (ADC_GetITStatus(ADCx,ADC_IT_EOC)==SET) { // 读取ADC的转换值 ADC_ConvertedValue = ADC_GetConversionValue(ADCx); } ADC_ClearITPendingBit(ADCx,ADC_IT_EOC); } ADC_ConvertedValue = ADC_GetConversionValue(ADCx); } ADC_ClearITPendingBit(ADCx,ADC_IT_EOC); } MX_ADC1_Init(); 对应 ADCx_GPIO_Config();ADCx_Mode_Config();ADC_NVIC_Config(); HAL_ADC_Init(&hadc1) 对应 ADC_Init(ADCx, &ADC_InitStructure) HAL_ADCEx_Calibration_Start(&hadc1); 对应 ADC_StartCalibration(ADCx); HAL_ADC_Start_IT(&hadc1); 对应 ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE); HAL_ADC_GetValue(hadc); 对应 ADC_GetConversionValue(ADCx); 七、注意事项 用户代码要加在 USER CODE BEGIN N 和 USER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1532 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1484 浏览 1 评论
901 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
649 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1528 浏览 2 评论
1841浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
589浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
490浏览 3评论
490浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
471浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-2 19:25 , Processed in 0.785394 second(s), Total 79, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号