实验要求:通过ADC1通道(PA1)采集电位器的电压,并显示ADC转换的数字量及换算后的电压值。
首先要确定最小刻度。
Vref=3.3V,所以输入电压有效范围在0V<=Vin<=3.3V,推导出最小刻度为 3.3V / 2^12(12位ADC)。
再确定转换时间。采样时间239.5个ADC时钟周期为例,可以得到转换时间为21us。
因为使用的是单通道,所以不使用扫描模式。因为未使用到DMA,所以使用单词扫描模式。
-----------------------------------------------------------------------------------------------------------------------
接下来介绍相关的HAL库函数:
1. HAL_ADC_Init():配置ADC工作参数;
2. HAL_ADCEx_Calibration_Start(): ADC校准;
3. HAL_ADC_MspInit(): 存放NVIC、CLOCK、GPIO初始化代码;
4.HAL_RCCEx_PeriphCLKConfig(): 设置扩展外设时钟,如:ADC、RTC等;
5. HAL_ADC_ConfigChannel(): 配置ADC相应通道的相关参数;
6. HAL_ADC_Start(): 启动A/D转换;
7. HAL_ADC_PollFortConversion(): 等待规则通道转换完成;
8. HAL_ADC_GetValue(): 获取规则通道通道A/D转换结果;
-----------------------------------------------------------------------------------------------------------------------
接下来介绍关键结构体:
1、ADC_HandleTypeDef g_adc_handle.Instance: ADC寄存器基地址;
2、g_adc_handle.Init.DataAlign: 数据对齐方式,有左/右对齐,一般选择右对齐;
3、g_adc_handle.Init.ScanConvMode: 扫描模式,此模式用于扫描一组模拟通道。通过ADC_CR1寄存器中的SCAN位置1来选择扫描模式。将此位置1后,ADC会扫描在ADC_SQRx寄存器(对于规则通道〉或ADC_JSQR寄存器(对于注入通道)中选择的所有通道。为组中的每个通道都执行一次转换。每次转换结束后,会自动转换该组中的下一个通道。如果将CONT位置1,规则通道转换不会在组中最后一个所选通道处停止,而是再次从第一个所选通道继续转换。如果将DMA位置1,则在每次规则通道转换之后,均使用直接存储器访问(DMA)控制器将转换自规则通道组的数据(存储在ADC_DR寄存器中)传输到SRAM。
4、g_adc_handle.Init.ContinuousConvMode: 开启单词转换模式或者连续转换模式。
单次转换模式:在单次转换模式下,ADC执行一次转换。CONT位为О时,可通过以下方式启动此模式;将ADC_CR2寄存器中的SWSTART位置1(仅适用于规则通道),将JSwSTART位置1(适用于注入通道),外部触发(适用于规则通道或注入通道),完成所选通道的转换之后:
如果转换了规则通道:转换数据存储在16位ADC_DR寄存器中,EOC(转换结束)标志置1,EOCIE位置1时将产生中断。
如果转换了注入通道:转换数据存储在16位ADC_JDR1寄存器中,JEOC(注入转换结束)标志置1,JEOCIE位置1时将产生中断。然后,ADC停止。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
连续转换模式:
在连续转换模式下,ADC结束一个转换后立即启动一个新的转换。CONT位为1时,可通过外部触发或将ADC_CR2寄存器中的SWSTRT位置1来启动此模式(仅适用于规则通道)。每次转换之后:
如果转换了规则通道组:上次转换的数据存储在16位ADC_DR寄存器中,EOC(转换结束)标志置1,EOCIE位置1时将产生中断,无法连续转换注入通道。连续模式下唯一的例外情况是,注入通道配置为在规则通道之后自动转换。
5、g_adc_handle.Init.NbrOfConversion: 设置转换通道数,此实验我们设置为1;
6、g_adc_handle.Init.DiscontinuousConvMode:是否使用规则通道组间断模式,一般不用;
7、g_adc_handle.Init.NbrOfDiscConversion:配置间断模式的规则通道个数,和上一个参数一起不设置
8、g_adc_handle.Init.ExternalTrigConv:ADC外部触发源选择,此次实验设置软件触发。
-----------------------------------------------------------------------------------------------------------------------
接下来介绍配置通道参数的结构体:
1、Channel: ADC转换通道;
2、Rank: ADC转换顺序;
3、SamplingTime: ADC采样周期
接下来编写实验代码:
首先编写函数文件adc.c:
#include "./BSP/ADC/adc.h"
ADC_HandleTypeDef g_adc_handle;
void adc_init(void){
ADC_ChannelConfTypeDef adc_ch_conf;
g_adc_handle.Instance = ADC1;
g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐
g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //不扫描
g_adc_handle.Init.ContinuousConvMode = DISABLE; //单次模式
g_adc_handle.Init.NbrOfConversion = 1; //转换通道数为1,单通道
g_adc_handle.Init.DiscontinuousConvMode = DISABLE; //不用间断模式
g_adc_handle.Init.NbrOfDiscConversion = 0; //无间断模式则无间断通道
g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //外部软件触发
HAL_ADC_Init(&g_adc_handle);
HAL_ADCEx_Calibration_Start(&g_adc_handle);
adc_ch_conf.Channel = ADC_CHANNEL_1;
adc_ch_conf.Rank = ADC_REGULAR_RANK_1; //转换顺序
adc_ch_conf.SamplingTime = ADC_SMAPLINGTIME_239CYCLES_5; //设置为最大值
HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);
}
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){
if(hadc->Instance == ADC1){
GPIO_InitTypeDef gpio_init_struct;
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能ADC时钟
__HAL_RCC_ADC1_CLK_ENABLE(); //使能GPIO时钟
gpio_init_struct.Pin = GPIO_PIN_1;
gpio_init_struct.Mode = GPIO_MODE_ANALOG; //模拟模式
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; //选择ADC外设时钟设置
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; //选择6分频,72/6=12MHz
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init, &g_adc_handle);
}
}
uint32_t adc_get_result(void){
HAL_ADC_Start(&g_adc_handle);
HAL_ADC_PollForConversion(&g_adc_handle, 10); //第二个参数比1大就行
return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}
uint32_t adc_get_result_average(uint32_t ch, uint8_t times){
uint32_t temp_val = 0;
uint8_t t;
for(t = 0; t < times; t++){
temp_val += adc_get_result();
delay_ms(5);
}
return temp_val / times;
}
再编写函数头文件adc.h:
#ifndef __ADC_H
#define __ADC_H
extern ADC_HandleTypeDef g_adc_handle;
void adc_init(void);
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc);
uint32_t adc_get_result(void);
uint32_t adc_get_result_average(uint32_t ch, uint8_t times);
#endif
最后编写主函数文件:
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/LED/led.h"
#include "./BSP/ADC/adc.h"
int main(void){
uint16_t adcx;
float temp;
HAL_Init();
sys_stm32_clock_init(RCC_PLL_MUL9);
delay_init(72);
usart_init(115200);
led_init();
lcd_init();
while(1){
adcx = adc_get_result_average(ADC_ADCX_CHY, 10);
lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE);
temp = (float)adcx * (3.3 / 4096); //获取带小数的测出电压值
adcx = temp; //把整数部分赋值给adcx(adcx为整型)
lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE); //显示整数部分
temp -= adcx; //去除整数部分
temp *= 1000; //获取保留的3位小数
lcd_show_xnum(150, 130, temp. 3, 16, 0x80, BLUE); //显示小数部分
LED0_TOGGLE();
delay_ms(100);
}
}
到此实验代码便编写完毕。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)