基于stm32的HAL库的adc采集实验
小实验1:ADC单通道采集实验
实验目的
使用 ADC1 采集通道 1 的电压值,通道 1 连接光敏电阻传感器。
adc功能代码模块
头文件
void adc_init(void);
uint32_t adc_get_result(uint32_t ch);
ADC_HandleTypeDef adc_handle = {0};
初始化
void adc_init(void)
{
adc_handle.Instance = ADC1; //通道选择
adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据对齐方式右对齐
adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //是否需要扫描,连续转换
adc_handle.Init.ContinuousConvMode =DISABLE; //连续模式 触发一次就连续不停的转换
adc_handle.Init.NbrOfConversion =1; //有几个需要转换
adc_handle.Init.DiscontinuousConvMode = DISABLE; //间断模式只有一个通道不需要间断
adc_handle.Init.NbrOfDiscConversion = 0;//触发方式
adc_handle.Init.ExternalTrigConv =ADC_SOFTWARE_STARTE;
HAL_ADC_Init(&adc_handle);
HAL_ADCEx_Calibration_Start(&adc_handle);//进行校准
}
底层代码配置NVIC、GPIO,CLOCK
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
if(hadc->Instance == ADC1)
{
RCC_PeriphCLKInitTypeDef adc_clk_init={0};
GPIO_InitTypeDef gpio_init_struct = {0};
__HAL_RCC_ADC1_CLK_ENABLE();//adc时钟初始化
__HAL_RCC_GPIO_CLK_ENABLE();//gpio时钟初始化
gpio_init_struct.Pin = GPIO_PIN_1;//只使用两个ADC有特供的
gpio_init_struct.Mode = GPIO_MODE_ANALOG;//模拟输入模式 adc dac,的特供
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;//时钟选择
//72分频后不能超过14所以选择6分频 12M
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
//通过指定参数初始化外设时钟
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
}
}
配置ADC通道
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank,uint32_t stime)
{
//单通道采集,通道封装 操作的哪个adc,指定那个通道ch 通道放在那个规则组中rank 哪个位置, 采集时间多少
ADC_ChannelConfTypeDef adc_ch_config = {0};
adc_ch_config.Channel = ch;//指定通道
adc_ch_config.Rank = rank;
adc_ch_config.SamPlingTime = stime;
HAL_ADC_ConfigChannel(hadc,&adc_ch_config);
}
//根据流程图调用一个GetValue的函数,这里只是进行进一步的封装
uint32_t adc_get_result(uint32_t ch)
{
//配置参数通道
adc_channel_config(&adc_handle ch,ADC_REGULAR_RANK_1,ADC_SAMPLETIME_239CYCLES_5);
//开启adc
HAL_ADC_Start(&adc_handle);
//判断是否转换完成
//获取DR寄存器中的值 等待时间
HAL_ADC_PollForConversion(&adc_handle,10);
return (uint16_t)HAL_ADC_GetValue(&adc_handle);
//adc1占换的结果放在DR寄存器的低16位,这里强转为uint16_t就可以直接获取。DR寄存器的高16位存放
// 的转换结果
}
main初始化
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"
void main(void)
{
HAL_Init();
stm32_clock_init(RCC_PLL_MUL9);
led_init();
uart1_init(115200);
adc_init();
printf("helloworld!\r\n");
while(1)
{
//一直答应adc的值
printf("adc_result: %f\r\n", (float)adc_get_result(ADC_CHANNEL_1) / 4096 *3.3);
delay_ms(500);
}
}
小实验2:ADC单通道采集实验+DMA读取
实验目的
使用 ADC1 采集通道 1 的电压值+DMA读取,通道 1 连接光敏电阻传感器
头文件
#include "sys.h"
void adc_dma_init(uint32_t *mar);//指定数据地址
ADC_HandleTypeDef adc_handle = {0};
DMA_HandleTypeDef dma_handle = {0};
初始化
void adc_config(void)
{
adc_handle.Instance = ADC1; //通道选择
adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据对齐
adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //是否需要扫描,连续的转换
adc_handle.Init.ContinuousConvMode=ENABLE;/*转换一次就将dma中的数字搬走,连续转化结婚,搬运这样就可以自动执行*/
adc_handle.Init.NbrOfConversion = 1; //有几个需要转换 单通道
adc_handle.Init.DiscontinuousConvMode = DISABLE;//间断模式只有一个通道不需要间断
adc_handle.Init.NbrOfDisConversion = 0;//触发方式
adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;//中断触发方式,一般软件
HAL_ADC_Init(&adc_handle);
//进行校对
HAL_ADCEx_Calibration_Start(&adc_handle);
}
底层代码配置NVIC、GPIO,CLOCK
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
if(hadc->Instance == ADC1)
{
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
GPIO_InitTypeDef gpio_init_struct = {0};
__HAL_RCC_ADC1_CLK_ENABLE();//adc时钟初始化
__HAL_RCC_GPIOA_CLK_ENABLE();//gpio时钟初始化
gpio_init_struct.Pin = GPIO_PIN_1;//只使用两个ADC有特供的
gpio_init_struct.Mode = GPIO_MODE_ANALOG;//模拟输入模式,adc,dac,的特供
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;//时钟选择
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;//72分频后不能超过14M所以选择6分频 12M
HAL_RCCEx_periphCLKConfig(&adc_clk_init);
}
}
通道配置
void dma_config(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
//提供的表格只适用于外设,串口1的TX是通道5
dma_handle.Instance = DMA1_Channel1; //指令基地址
dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; //指定方向 外设到内存
//内存相关配置
//数据对齐的方式 一般用8字节对齐
//对其增长方式
dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;//adc的DR寄存器是16位的,半字是16位的
dma_handle.Init.MemInc = DMA_MINC_ENABLE;//指针往下偏移,如果不偏移每次转运的都是第一个字符,地址递增
//外设相关配置
//外设对齐的方式 一般用8字节对齐
//外设对齐增长方式
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;;//外设地址不能递增,如果递增,数据传数过来地址就跑到其他地方了,每次发到TDR寄存器内,不能递增
dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM; //如果只哟一个通道在使用优先级无所谓
dma_handle.Init.Mode = DMA_CIRCULAR;//传输模式 内存到内存只支持普通的模式,不支持循环模式
HAL_DMA_Init(&dma_handle);
//链接_HAL_LINK_DMA 是一个宏函数, 参数 需要连接的通道句柄,句柄中对应的成员变量,DMA的句柄
__HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);//在函数中将句柄与dma建立关联
}
单通道采集,通道封装 操作的哪个adc,指定那个通道ch 通道放在那个规则组中rank 哪个位置, 采集时间多少
//单通道采集,通道封装 操作的哪个adc,指定那个通道ch 通道放在那个规则组中rank 哪个位置, 采集时间多少
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch,uint32_t rank,uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_config = {0};
adc_ch_config.Channel = ch;//指定通道
adc_ch_config.Rank = rank;
adc_ch_config.SamplingTime = stime;
HAL_ADC_ConfigChannel(hadc,&adc_ch_config);
}
由于是adc_dma也要初始化
//由于是adc_dma也要初始化
void adc_dma_init(uint32_t *mar)//指定数据地址
{
adc_config();
//adc 通道填进去 (序列号,流水线的哪个位置) adc采样的时间
adc_channel_config(&adc_handle,ADC_CHANNEL_1,ADC_REGULAR_RANK_1,ADC_SAMPLETIME_239CYCLES_5);
dma_config();
//触发adc转换,DMA传输数据
HAL_ADC_Start_DMA(&adc_handle,mar,1);//只转运一个就可以
}