做网站有哪些需求,友情链接出售网,别人做的网站不能用,优化的含义STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU STM32-14-FSMC_LCD STM32-15-DMA…STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU STM32-14-FSMC_LCD STM32-15-DMA 文章目录 STM32-16-ADC1. ADC简介2. ADC工作原理3. 单通道ADC采集实验1. 相关寄存器2. 配置步骤3. 代码实现 4. 单通道ADC采集(DMA读取)实验5. 多通道ADC采集(DMA读取)实验6. 单通道ADC过采样实验7. 内部温度传感器实验8. 光敏传感器实验 STM32-16-ADC
1. ADC简介 什么是ADC ADCAnalog-to-digital converter即模拟/数字转换器可以将外部的模拟信号转换为数字信号。STM32F103系列芯片拥有3个ADC(C8T6只有2个)这些ADC可以独立使用其中 ADC1和ADC2还可以组成双重模式提高采样率。STM32的ADC是12位逐次逼近型的模拟数字转换器。它有18个通道可测量16个外部和2个内部信号源其中ADC3根据CPU引脚的不同其通道数也不同一般有8个外部通道。ADC中的各个通道的AD转换可以单次、连续、扫描或间断模式执行。ADC的结果可以以左对齐或者右对齐存储在16位数据寄存器中。 在嵌入式系统中ADC通常用于采集传感器的数据例如压力、温度、图像传感器等参数被转换成电压后通过ADC转换为数字量供单片机处理单片机根据处理结果进行相关控制和显示。 主要特性 12位分辨率转换结束、注入转换结束和发生模拟看门狗时产生中断单次和连续转换模式自校准带内嵌数据一致性的数据对齐采样间隔可以按通道分别编程规则转换和注入转换均有外部触发选项间断模式双重模式ADC转换时间时钟为72MHz为1.17usADC供电要求2.4V到3.6VADC输入范围Vref-VinVref规则通道转换期间有DMA请求产生 常见的ADC类型: ADC电路类型优点缺点并联比较型转换速度最快成本高、功耗高分辨率低逐次逼近型结构简单功耗低转换速度较慢并联比较型Flash型和逐次逼近型SAR型是两种常见的模数转换器ADC架构它们在工作原理、速度、功耗、精度等方面有明显的区别。 并联比较型ADCFlash ADC 工作原理 并联比较型ADC使用一组并行的比较器将输入模拟信号同时与参考电压分压网络进行比较。每个比较器对应一个特定的电压范围输出一个二进制码。比较器的输出直接决定ADC的数字输出无需逐步逼近。 特点 速度快由于所有比较器同时工作转换时间非常短适合高频信号采样。高功耗大量并行比较器和电阻网络消耗较多电能。高复杂度电路复杂度随分辨率成指数增长如8位ADC需要255个比较器。应用场景高速数据采集、视频处理、通信系统等。 逐次逼近型ADCSAR ADC 工作原理 逐次逼近型ADC通过一个逐次逼近寄存器SAR和一个比较器工作。它逐步逼近输入模拟信号从最高位到最低位逐位进行比较。每次比较后根据比较结果决定下一个比较的方向直到所有位都确定。 特点 速度适中转换速度适中比Flash ADC慢但比积分型ADC快。低功耗由于只使用一个比较器功耗较低。高分辨率适合高分辨率应用电路复杂度与分辨率成线性关系。应用场景低功耗应用、音频采样、精密测量仪器等。 特点并联比较型ADC逐次逼近型ADC转换速度非常快纳秒级适中微秒级功耗高低电路复杂度高比较器数量多适中逐次逼近寄存器分辨率适中通常8位或以下高可达16位或以上典型应用高速数据采集、视频处理音频采样、精密测量 并联比较型ADC适合需要极高转换速度的应用而逐次逼近型ADC则在功耗和分辨率之间提供了良好的平衡适合多种精密测量和中速数据采集的应用。 ADC的特性参数 分辨率 表示ADC能辨别的最小模拟量用二进制位表示转换时间 表示完成一次A/D转换所需要的时间转换时间越短采样率越高精度 最小刻度基础上叠加各种误差的参数精度受ADC性能、温度和气压等影响量化误差 用数字量近似表示模拟量采用四舍五入原则此过程产生的误差为量化误差
2. ADC工作原理
ADC框图 ①输入电压 ②输入通道 确定好ADC输入电压后通过ADC输入通道把外部电压输送到ADC转换器中下面是ADC1~3对应的通道。 ③转换顺序 在STM32的ADC转换过程中规则组和注入组是两种不同的ADC转换序列它们在配置、使用场景和优先级等方面存在显著区别。 规则组Regular Group 定义和特点 规则组是常规的ADC转换序列通常用于连续的、周期性的ADC采样。可以包含多个通道这些通道按照配置的顺序依次进行转换。规则组的转换可以由多种触发源启动如软件触发、定时器触发等。 应用场景 常用于采集传感器数据、定期采样信号等场景。适用于实时性要求较高的应用但相对注入组来说优先级较低。 触发源 支持多种触发源包括软件触发和硬件触发如定时器事件。可设置为连续转换模式或单次转换模式。 注入组Injected Group 定义和特点 注入组是为了在规则转换之外进行临时或优先转换的一种机制。通常包含少量通道用于处理突发性或优先级更高的ADC采样需求。注入组转换有独立的触发源与规则组转换互不干扰。 应用场景 常用于优先级更高的采样需求如监测关键参数的变化、快速响应事件等。注入组的转换结果可以通过中断或DMA方式快速处理适用于突发事件的处理。 触发源 注入组有独立的触发源可以是外部事件、定时器触发等。通常由特定的事件触发不会像规则组那样进行连续转换。 总结 特点规则组Regular Group注入组Injected Group转换触发多种触发源软件、定时器等独立触发源外部事件、定时器等转换顺序配置的通道顺序配置的少量优先通道优先级相对较低相对较高应用场景定期采样、传感器数据采集关键参数监测、突发事件处理转换模式连续或单次转换通常为单次转换结果处理常规方式处理可通过中断或DMA快速处理 规则序列
规则组最多允许16个输入通道进行转换那么就需要设置通道转换的顺序即规则序列。
规则序列寄存器控制关系如下:
注入序列
注入组最大允许4个通道输入它的注入序列由JSQR寄存器配置。
注入序列寄存器控制关系如下 ④触发源 规则组外部触发使用方法是将 EXTTRIG 位置 1并且通过 EXTSET[2:0]位选择规则组启动转换的触发源。如果 EXTSET[2:0]位设置为 111那么可以通过 SWSTART 为启动 ADC 转换 相当于软件触发。 注入组外部触发使用方法是将 JEXTTRIG 位置 1并且通过 JEXTSET[2:0]位选择注入组启动转换的触发源。如果 JEXTSET[2:0]位设置为 111那么可以通过 JSWSTART 为启动 ADC 转换相当于软件触发。 ⑤转换时间 ADC的输入时钟是由PCLK2经过分频产生的分频系数是由RCC_CFGR寄存器的ADCPRE[1:0]位设置的可选择 2/4/8/16 分频。需要注意的是ADC 的输入时钟频率最大值是14MHz如果超过这个值将会导致 ADC 的转换结果准确度下降。 一般我们设置 PCLK2 为 72MHz。为了不超过 ADC 的最大输入时钟频率 14MHz我们设置 ADC 的预分频器分频系数为 6就可以得到 ADC 的输入时钟频率为 72MHz/6即 12MHz。 ⑥数据寄存器 ADC 规则数据寄存器ADC_DR ADC规则组数据寄存器ADC_DR是一个32位的寄存器独立模式时只使用到该寄存器低16位保存ADC1/2/3的规则转换数据。在双ADC模式下高16位用于保存ADC2转换的数据低16位用于保存ADC1转换的数据。 因为ADC的精度是12位的ADC_DR寄存器无论高16位还是低16w位存放数据的位宽都是16位的所以允许选择数据对齐方式。由ADC_CR2寄存器的ALIGN位设置数据对齐方式可选择右对齐或者左对齐。 规则组16个输入通道只对应一个数据寄存器所以通道转换完成后要及时取出数据比较常用的方法是使用DMA模式。当规则组的转换结束后就会产生DMA请求这样就可以把数据及时搬运到用户指定的目的地址存放。只有ADC1和ADC3可以产生DMA请求而ADC2转换的数据可以通过双ADC模式利用ADC1的DMA功能传输。 ADC 注入数据寄存器xADC_JDRx(x1~4) ADC注入数据寄存器有4个注入组最多有4个输入通道刚好每个通道都有自己对应的数据寄存器。ADC_JDRx寄存器是32位的低16位有效高16位保留数据也同样需要选择对齐方式。也是由ADC _CR2寄存器的ALIGN位设置数据对齐方式可选择右对齐或者左对齐。 ⑦中断 中断事件事件标志使能控制位规则通道转换结束EOCEOCIE注入通道转换结束JEOCJEOCIE设置了模拟看门狗状态位AWDAWDIE规则组和注入组的转换结束后除了可以产生中断外还可以产生 DMA 请求我们利用DMA 及时把转换好的数据传输到指定的内存里防止数据被覆盖。 ⑧单次转换模式和连续转换模式 ⑨扫描模式
3. 单通道ADC采集实验
1. 相关寄存器 ADC控制寄存器1ADC_CR1 ADC控制寄存器2ADC_CR2 ADC采样事件寄存器1ADC_SMPR1 ADC采集事件寄存器2ADC_SMPR2 ADC 采样时间设置需要由两个寄存器设置ADC_SMPR1 和 ADC_SMPR2分别设置通道 10~17 和通道 0~9 的采样时间每个通道用 3 个位设置。 ADC规则序列寄存器1ADC_SQR1 ADC_SQR2 ADC_SQR3 L[3:0]用于设置规则组序列的长度取值范围015表示规则组的长度是116。本实验只用了1个输入通道所以L[3:0]位设置为0000即可。 SQ13[4:0]~SQ16[4:0]位设置规则组序列的第 13~16 个转换编号第 1~12 个转换编号的设置请查看 ADC_SQR2 和 ADC_SQR3 寄存器。 ADC规则数据寄存器ADC_DR ADC状态寄存器ADC_SR
2. 配置步骤 配置ADC工作参数、ADC校准 HAL_ADC_Init()
HAL_ADCEx_Calibration_Start()ADC MSP初始化 HAL_ADC_MspInit() 配置ADC相应通道相关参数 HAL_ADC_ConfigChannel()启动A/D转换 HAL_ADC_Start()等待规则通道转换完成 HAL_ADC_PollForConversion()获取规则通道A/D转换结果 HAL_ADC_GetValue()具体函数功能 函数主要寄存器主要功能HAL_ADC_Init()CR1、CR2配置ADC工作参数HAL_ADCEx_Calibration_Start()CR2ADC校准HAL_ADC_MspInit()无存放NVIC、CLOCK、GPIO初始化代码HAL_RCCEx_PeriphCLKConfig()RCC_CFGR设置扩展外设时钟如ADC、RTC等HAL_ADC_ConfigChannel()SQRx、SMPRx配置ADC相应通道的相关参数HAL_ADC_Start()CR2启动A/D转换HAL_ADC_PollForConversion()SR等待规则通道转换完成HAL_ADC_GetValue()DR获取规则通道A/D转换结果
3. 代码实现 功能 采集ADC1通道1(PA1)上面的电压并在LCD模块上面显示ADC规则数据寄存器12位的转换值以及将该值换算成电压后的电压值。使用杜邦线将ADC和RV1排针连接使得PA1连接到电位器上然后将ADC采集到的数据和转换后的电压值在TFTLCD屏中显示。用户可以通过调节电位器的旋钮改变电压值。LED0闪烁提示程序运行。 ADC单通道初始化函数 void adc_init(void)
{ADC_ChannelConfTypeDef adc_ch_conf;g_adc_handle.Instance ADC1; //选择ADC1g_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); //校准ADCadc_ch_conf.Channel ADC_CHANNEL_1; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_1; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_handle, adc_ch_conf); //通道配置
}g_adc_handle.Instance ADC1;: 选择ADC1作为转换的ADC模块。 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;: 仅使用一个转换通道。 g_adc_handle.Init.DiscontinuousConvMode DISABLE;: 禁用间断模式。 g_adc_handle.Init.NbrOfDiscConversion 0;: 设置间断模式的规则通道数为0。 g_adc_handle.Init.ExternalTrigConv ADC_SOFTWARE_START;: 设置软件触发转换。 adc_ch_conf.Channel ADC_CHANNEL_1;: 选择ADC通道1。 adc_ch_conf.Rank ADC_REGULAR_RANK_1;: 设置通道1为规则组的第一个转换通道。 adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5;: 采样时间为239.5个ADC时钟周期。 STM32的ADC1模块进行初始化和通道配置为后续的ADC数据采集做好准备。具体来说初始化配置包括设置数据对齐方式、转换模式和触发方式等。通过校准确保ADC转换的精度然后配置一个规则组通道使得ADC可以在指定通道上进行采样并转换。 ADC MSP初始化函数 void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc-Instance ADC1){GPIO_InitTypeDef gpio_init_struct;RCC_PeriphCLKInitTypeDef adc_clk_init {0};//使能GPIOA和ADC1的时钟__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_ADC1_CLK_ENABLE();// 配置GPIOA的引脚1为模拟输入模式gpio_init_struct.Pin GPIO_PIN_1;gpio_init_struct.Mode GPIO_MODE_ANALOG; //模拟输入HAL_GPIO_Init(GPIOA, gpio_init_struct);//配置ADC的时钟源和分频系数adc_clk_init.PeriphClockSelection RCC_PERIPHCLK_ADC; //ADC外设时钟adc_clk_init.AdcClockSelection RCC_ADCPCLK2_DIV6;//分频因子6时钟为72M/612MHz HAL_RCCEx_PeriphCLKConfig(adc_clk_init); //设置ADC时钟}
}配置ADC1的时钟源和分频系数。这里选择了RCC的ADC外设时钟并将ADC的时钟频率设置为系统时钟72MHz除以6即12MHz。这有助于确保ADC1以适当的频率运行从而保证其性能和精度。 启动ADC并获取转换结果函数 uint32_t adc_get_result(void)
{// 启动A/D转换HAL_ADC_Start(g_adc_handle); // 开启ADC// 等待规则通道转换完成HAL_ADC_PollForConversion(g_adc_handle, 10); // 轮询转换等待完成// 获取规则通道A/D转换结果return (uint16_t)HAL_ADC_GetValue(g_adc_handle); // 返回最近一次规则组的转换结果
}启动A/D转换: HAL_ADC_Start(g_adc_handle);g_adc_handle 是先前定义的并初始化好的 ADC 句柄。此操作会开始对选定的ADC通道进行模数转换。 等待规则通道转换完成: HAL_ADC_PollForConversion(g_adc_handle, 10);调用 HAL_ADC_PollForConversion 函数进行轮询等待转换完成。这里设置了一个超时值为10个时钟周期。如果在超时时间内转换完成函数会返回并继续执行否则它会阻塞直到超时或者转换完成。 获取规则通道A/D转换结果: return (uint16_t)HAL_ADC_GetValue(g_adc_handle);调用 HAL_ADC_GetValue 函数获取ADC转换结果。返回的值是最近一次转换的结果类型为 uint16_t因为一般ADC的结果是16位。 主函数 int main(void)
{uint16_t adcx; //ADC换算后的数字量float temp; //转换后的电压值HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */oled_init(); //OLED初始化adc_init();lcd_show_string(110, 50, 240, 16, 32, STM32, GREEN);lcd_show_string(105, 90, 240, 16, 24, ADC TEST, GREEN);lcd_show_string(30, 150, 240, 16, 24, CH1_VAL:, BLUE);lcd_show_string(30, 180, 240, 16, 24, CH1_VOL:0.000V, BLUE);oled_show_string(10,0,ADC TEST,24);oled_show_string(0, 25, CH1_VAL:, 16);oled_show_string(0, 41, CH1_VOL:0.000V, 16);oled_refresh_gram();while (1){adcx adc_get_result(); //获取ADC转换后的结果lcd_show_xnum(126, 150, adcx, 5, 24, 0, BLUE);oled_show_num(65, 25, adcx, 5, 16);//在LCD和OLED上显示电压值整数部分temp (float)adcx * (3.3 / 4096);adcx temp;lcd_show_xnum(126, 180, adcx, 1, 24, 0, BLUE);oled_show_num(64, 41, adcx, 1, 16);//在LCD和OLED上显示电压值小数部分temp - adcx;temp * 1000;lcd_show_xnum(150, 180, temp, 3, 24, 0x80, BLUE);oled_show_num(80, 41, temp, 3, 16);LED0_TOGGLE(); delay_ms(100);oled_refresh_gram();}
}实验结果 作用 实现ADC转换并将结果显示在LCD和OLED上实时显示ADC通道的数值和对应的电压值。 影响 可以实时监控ADC通道的输入电压适用于需要监测模拟信号的应用如传感器数据读取。通过LED的切换可以直观地知道系统是否在运行。延时和轮询会占用CPU资源对实时性有一定影响如果有更高的实时性要求可以考虑使用中断或DMA。
4. 单通道ADC采集(DMA读取)实验 功能 使用 ADC 采集DMA 读取通道 1PA1上面的电压在 LCD 和OLED模块上面显示 ADC 转换值以及换算成电压后的电压值。使用短路帽将 ADC 和 RV1 排针连接使得 PA1 连接到电位器上然后将 ADC 采集到的数据和转换后的电压值在 TFTLCD 屏中显示。用户可以通过调节电位器的旋钮改变电压值。LED0 闪烁提示程序运行。 ADC_DMA初始化函数 void adc_dma_init(uint32_t mar)
{ADC_ChannelConfTypeDef adc_ch_conf {0};__HAL_RCC_DMA1_CLK_ENABLE();g_dma_adc_handle.Instance DMA1_Channel1;g_dma_adc_handle.Init.Direction DMA_PERIPH_TO_MEMORY;g_dma_adc_handle.Init.PeriphInc DMA_PINC_DISABLE;g_dma_adc_handle.Init.MemInc DMA_MINC_ENABLE;g_dma_adc_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_HALFWORD;g_dma_adc_handle.Init.MemDataAlignment DMA_MDATAALIGN_HALFWORD;g_dma_adc_handle.Init.Mode DMA_NORMAL;g_dma_adc_handle.Init.Priority DMA_PRIORITY_MEDIUM; HAL_DMA_Init(g_dma_adc_handle);//2.将DMA和ADC句柄连接起来__HAL_LINKDMA(g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);g_adc_dma_handle.Instance ADC1; //选择ADC1g_adc_dma_handle.Init.DataAlign ADC_DATAALIGN_RIGHT; //数据对齐方式为右对齐g_adc_dma_handle.Init.ScanConvMode ADC_SCAN_DISABLE; //非扫描模式仅使用一个通道g_adc_dma_handle.Init.ContinuousConvMode ENABLE; //打开连续转换模式g_adc_dma_handle.Init.NbrOfConversion 1; //通道数选择1个g_adc_dma_handle.Init.DiscontinuousConvMode DISABLE; //禁止规则通道组间断模式 g_adc_dma_handle.Init.NbrOfDiscConversion 0; //配置间断模式的规则通道个数g_adc_dma_handle.Init.ExternalTrigConv ADC_SOFTWARE_START; //软件触发方式HAL_ADC_Init(g_adc_dma_handle); //进行初始化HAL_ADCEx_Calibration_Start(g_adc_dma_handle); //校准ADCadc_ch_conf.Channel ADC_CHANNEL_1; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_1; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_dma_handle, adc_ch_conf); //通道配置HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);HAL_DMA_Start_IT(g_dma_adc_handle, (uint32_t)ADC1-DR, mar, 0);HAL_ADC_Start_DMA(g_adc_dma_handle, mar, 0);
}总结 启用DMA1时钟 __HAL_RCC_DMA1_CLK_ENABLE();使能DMA1的时钟确保DMA1能够正常工作。 配置DMA g_dma_adc_handle.Instance DMA1_Channel1;
g_dma_adc_handle.Init.Direction DMA_PERIPH_TO_MEMORY; // 数据从外设到内存
g_dma_adc_handle.Init.PeriphInc DMA_PINC_DISABLE; // 外设地址不递增
g_dma_adc_handle.Init.MemInc DMA_MINC_ENABLE; // 内存地址递增
g_dma_adc_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_HALFWORD; // 外设数据对齐为16位
g_dma_adc_handle.Init.MemDataAlignment DMA_MDATAALIGN_HALFWORD; // 内存数据对齐为16位
g_dma_adc_handle.Init.Mode DMA_NORMAL; // 普通模式
g_dma_adc_handle.Init.Priority DMA_PRIORITY_MEDIUM; // 优先级为中等
HAL_DMA_Init(g_dma_adc_handle);初始化DMA1的通道1配置DMA的数据传输方向、地址递增模式、数据对齐方式、传输模式和优先级。 将DMA和ADC句柄连接起来 __HAL_LINKDMA(g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);将DMA句柄与ADC句柄连接使ADC能够使用DMA传输数据。 初始化ADC g_adc_dma_handle.Instance ADC1; // 选择ADC1
g_adc_dma_handle.Init.DataAlign ADC_DATAALIGN_RIGHT; // 数据右对齐
g_adc_dma_handle.Init.ScanConvMode ADC_SCAN_DISABLE; // 非扫描模式
g_adc_dma_handle.Init.ContinuousConvMode ENABLE; // 连续转换模式
g_adc_dma_handle.Init.NbrOfConversion 1; // 单通道
g_adc_dma_handle.Init.DiscontinuousConvMode DISABLE; // 禁止间断模式
g_adc_dma_handle.Init.NbrOfDiscConversion 0; // 间断模式转换数
g_adc_dma_handle.Init.ExternalTrigConv ADC_SOFTWARE_START; // 软件触发
HAL_ADC_Init(g_adc_dma_handle);配置并初始化ADC1包括数据对齐方式、扫描模式、连续转换模式、转换通道数、间断模式和触发方式。 校准ADC HAL_ADCEx_Calibration_Start(g_adc_dma_handle);启动ADC的校准以提高转换精度。 配置ADC通道 adc_ch_conf.Channel ADC_CHANNEL_1; // 通道1
adc_ch_conf.Rank ADC_REGULAR_RANK_1; // 规则组第1个转换
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; // 采样时间
HAL_ADC_ConfigChannel(g_adc_dma_handle, adc_ch_conf);配置ADC的输入通道、转换序列和采样时间。 配置DMA中断优先级并启用中断 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);设置DMA1通道1中断的优先级并启用中断。 启动DMA传输和ADC DMA模式 HAL_DMA_Start_IT(g_dma_adc_handle, (uint32_t)ADC1-DR, mar, 0);
HAL_ADC_Start_DMA(g_adc_dma_handle, mar, 0);启动DMA传输将ADC数据寄存器的地址作为源地址mar作为目标地址并指定传输数据量。启动ADC的DMA模式使得ADC转换完成后通过DMA传输数据。 代码的功能 使能DMA和ADC时钟确保DMA和ADC正常工作。配置DMA设置DMA传输的方向、地址递增模式、数据对齐方式、传输模式和优先级。初始化ADC配置ADC的工作模式、转换方式和触发方式。校准ADC提高ADC转换的精度。配置ADC通道指定ADC的输入通道和采样时间。配置中断设置DMA中断的优先级并启用中断以便在数据传输完成后处理中断。启动DMA传输和ADC DMA模式实现ADC转换结果通过DMA自动传输到指定内存地址提高数据传输效率和系统性能。 对程序的影响 提高数据传输效率使用DMA自动传输数据减少CPU的负担。自动化数据采集通过DMA实现ADC数据的自动传输无需手动处理数据搬运。实时数据处理配置中断处理机制可以在数据传输完成后立即处理提高系统的实时性和响应速度。 ADC MSP初始化函数 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();__HAL_RCC_ADC1_CLK_ENABLE();//配置工作模式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时钟为72M/612MHz HAL_RCCEx_PeriphCLKConfig(adc_clk_init); //设置ADC时钟}
}使能一次ADC DMA传输函数 void adc_dma_enable(uint16_t cndtr)
{ADC1-CR2 ~(1 0); //CONT位置1,关闭连续转换模式DMA1_Channel1-CCR ~(1 0); //禁用DMA通道1while(DMA1_Channel1-CCR (1 0)); //等待DMA通道1禁用完成DMA1_Channel1-CNDTR cndtr; //设置DMA传输数据量DMA1_Channel1-CCR | 1 0; //启用DMA通道1ADC1-CR2 | 1 0; //开启ADC1ADC1-CR2 | 1 22; //启用ADC1的DMA请求
}总结 关闭ADC连续转换模式 ADC1-CR2 ~(1 0);清除ADC1控制寄存器2CR2的第0位关闭连续转换模式。 禁用DMA通道1 DMA1_Channel1-CCR ~(1 0);清除DMA1通道1控制寄存器CCR的第0位禁用DMA通道1。 等待DMA通道1禁用完成 while(DMA1_Channel1-CCR (1 0));使用while循环等待直到DMA1通道1控制寄存器的第0位被清除确保DMA通道1已完全禁用。 设置DMA传输数据量 DMA1_Channel1-CNDTR cndtr;将传入的参数 cndtr 赋值给DMA1通道1的数据传输数量寄存器CNDTR指定DMA传输的数据量。 启用DMA通道1 DMA1_Channel1-CCR | 1 0;设置DMA1通道1控制寄存器的第0位启用DMA通道1。 开启ADC1 ADC1-CR2 | 1 0;设置ADC1控制寄存器2CR2的第0位开启ADC1。 启用ADC1的DMA请求ADC1-CR2 | 1 22;设置ADC1控制寄存器2CR2的第22位启用ADC1的DMA请求。这样ADC转换完成后将自动通过DMA将数据传输到指定的存储位置。 作用 启用DMA通过DMA自动传输ADC转换的数据减少CPU的负担提高数据传输效率。启用ADC连续转换模式让ADC能够连续进行多次转换而无需每次都启动转换。- ADC DMA采集中断服务函数 void DMA1_Channel1_IRQHandler(void)
{if(DMA1-ISR (11)){g_adc_dma_sta 1;//清除中断标志位DMA1-IFCR | (1 1);}}总结 中断处理 该函数是DMA1通道1的中断处理函数。当DMA1通道1触发中断时系统会调用这个函数。 检查中断标志 if(DMA1-ISR (11))检查DMA1的中断状态寄存器ISR的第1位是否被置位。这一位表示DMA1通道1的传输完成中断。如果该位被置位说明DMA1通道1完成了数据传输。 设置状态标志 g_adc_dma_sta 1;设置一个全局标志位 g_adc_dma_sta表示DMA传输已完成。这个标志位可以在主程序或者其他地方用来判断DMA传输状态。 清除中断标志 DMA1-IFCR | (1 1);清除DMA1通道1的传输完成中断标志位。这一步是必须的否则中断处理程序会再次触发。 作用总结 响应DMA传输完成中断当DMA1通道1完成数据传输时会触发中断。设置传输完成标志通过设置 g_adc_dma_sta主程序或其他代码可以检测到DMA传输已完成并进行后续处理。清除中断标志位清除中断标志位以便系统能处理下一次DMA传输完成中断。 主函数 int main(void)
{uint16_t i;uint16_t adcx;uint32_t sum;float temp;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */oled_init();adc_dma_init((uint32_t)g_adc_dma_buf); /* 初始化ADC DMA采集 */oled_show_string(10,0,ADC TEST,24);oled_show_string(0, 25, CH1_VAL:, 16);oled_show_string(0, 41, CH1_VOL:0.000V, 16);oled_refresh_gram();lcd_show_string(110, 50, 240, 16, 32, STM32, GREEN);lcd_show_string(105, 90, 240, 16, 24, ADC TEST, GREEN);lcd_show_string(30, 150, 240, 16, 24, CH1_VAL:, BLUE);lcd_show_string(30, 180, 240, 16, 24, CH1_VOL:0.000V, BLUE);adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动ADC DMA采集 */while (1){if (g_adc_dma_sta 1){/* 计算DMA 采集到的ADC数据的平均值 */sum 0;for (i 0; i ADC_DMA_BUF_SIZE; i) /* 累加 */{sum g_adc_dma_buf[i];printf(%d\r\n, sum);}adcx sum / ADC_DMA_BUF_SIZE; /* 取平均值 *//* 显示结果 */lcd_show_xnum(126, 150, adcx, 5, 24, 0, BLUE); /* 显示ADCC采样后的原始值 */oled_show_num(65, 25, adcx, 5, 16);temp (float)adcx * (3.3 / 4096); /* 获取计算后的带小数的实际电压值比如3.1111 */adcx temp; /* 赋值整数部分给adcx变量因为adcx为u16整形 */lcd_show_xnum(126, 180, adcx, 1, 24, 0, BLUE); /* 显示电压值的整数部分3.1111的话这里就是显示3 */oled_show_num(64, 41, adcx, 1, 16);temp - adcx; /* 把已经显示的整数部分去掉留下小数部分比如3.1111-30.1111 */temp * 1000; /* 小数部分乘以1000例如0.1111就转换为111.1相当于保留三位小数。 */lcd_show_xnum(150, 180, temp, 3, 24, 0x80, BLUE); /* 显示小数部分前面转换为了整形显示这里显示的就是111. */oled_show_num(80, 41, temp, 3, 16);g_adc_dma_sta 0; /* 清除DMA采集完成状态标志 */adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动下一次ADC DMA采集 */}LED0_TOGGLE();delay_ms(100);oled_refresh_gram();}
}总结 adc_dma_init((uint32_t)g_adc_dma_buf); /* 初始化ADC DMA采集 */初始化ADC和DMA使得ADC可以通过DMA方式进行数据采集采集的数据存储在 g_adc_dma_buf 缓冲区中。 adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动ADC DMA采集 */启动ADC DMA采集使得ADC开始连续采集数据并通过DMA传输到缓冲区 g_adc_dma_buf。 代码通过初始化和配置STM32的各种外设实现了ADC数据的DMA采集并在LCD和OLED上显示采集到的ADC数据和转换后的电压值。同时通过中断和DMA机制实现了高效的数据采集和处理。 什么时候采集什么时候传输 ADC采集的控制 ADC的连续转换模式被启用g_adc_dma_handle.Init.ContinuousConvMode ENABLE;。这意味着ADC会连续地进行转换。但是ADC的转换触发和数据读取是通过DMA控制的。当你调用adc_dma_enable函数时ADC和DMA都被启动并开始进行数据采集和传输。 DMA传输的控制 当你停止DMA传输例如通过禁用DMA通道ADC可能会继续进行转换但是这些转换结果不会通过DMA传输到内存。这是因为DMA传输是负责将ADC转换结果传输到指定的内存缓冲区g_adc_dma_buf。如果DMA被禁用但ADC继续进行转换结果仍会存储在ADC的数据寄存器DR中但不会被传输到内存。
5. 多通道ADC采集(DMA读取)实验 功能使用ADC1采集(DMA读取)通道1/2/3/4/5/6的电压在LCD模块上面显示对应的ADC转换值以及换算成电压后的电压值。可以使用杜邦线连接PA0/PA1/PA2/PA3/PA4/PA5到你想测量的电压源(0~3.3V)然后通过TFTLCD显示的电压值。LED0闪烁提示程序运行。 多通道ADC初始化函数 void adc_nch_dma_init(uint32_t mar)
{ADC_ChannelConfTypeDef adc_ch_conf {0};__HAL_RCC_DMA1_CLK_ENABLE(); //使能DMA1时钟g_dma_adc_handle.Instance DMA1_Channel1; //初始化DMA1的通道1g_dma_adc_handle.Init.Direction DMA_PERIPH_TO_MEMORY; //数据传输方向设置为外设到内存g_dma_adc_handle.Init.PeriphInc DMA_PINC_DISABLE; //外设地址不递增g_dma_adc_handle.Init.MemInc DMA_MINC_ENABLE; //内存地址递增g_dma_adc_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_HALFWORD; //数据对齐使用半字g_dma_adc_handle.Init.MemDataAlignment DMA_MDATAALIGN_HALFWORD; //数据对齐使用半字g_dma_adc_handle.Init.Mode DMA_NORMAL; //传输模式配置为正常模式g_dma_adc_handle.Init.Priority DMA_PRIORITY_MEDIUM; //优先级设置为中等HAL_DMA_Init(g_dma_adc_handle);//2.将DMA和ADC句柄连接起来__HAL_LINKDMA(g_adc_nch_dma_handle, DMA_Handle, g_dma_adc_handle);g_adc_nch_dma_handle.Instance ADC1; //选择ADC1g_adc_nch_dma_handle.Init.DataAlign ADC_DATAALIGN_RIGHT; //数据对齐方式为右对齐g_adc_nch_dma_handle.Init.ScanConvMode ADC_SCAN_ENABLE; //扫描模式多个通道g_adc_nch_dma_handle.Init.ContinuousConvMode ENABLE; //打开连续转换模式g_adc_nch_dma_handle.Init.NbrOfConversion 6; //通道数选择1个g_adc_nch_dma_handle.Init.DiscontinuousConvMode DISABLE; //禁止规则通道组间断模式 g_adc_nch_dma_handle.Init.NbrOfDiscConversion 0; //配置间断模式的规则通道个数g_adc_nch_dma_handle.Init.ExternalTrigConv ADC_SOFTWARE_START; //软件触发方式HAL_ADC_Init(g_adc_nch_dma_handle); //进行初始化HAL_ADCEx_Calibration_Start(g_adc_nch_dma_handle); //校准ADCadc_ch_conf.Channel ADC_CHANNEL_0; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_1; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_1; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_2; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_2; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_3; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_3; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_4; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_4; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_5; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_5; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_6; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);HAL_DMA_Start_IT(g_dma_adc_handle, (uint32_t)ADC1-DR, mar, 0);HAL_ADC_Start_DMA(g_adc_nch_dma_handle, mar, 0);
}多通道ADC转换与单通道ADC转换的区别 DMA的相关配置不需要改动 ADC的配置要使能扫描模式通道数设置为6个 g_adc_nch_dma_handle.Init.ContinuousConvMode ENABLE; //打开连续转换模式
g_adc_nch_dma_handle.Init.NbrOfConversion 6; //通道数选择6个设置6个通道序列 adc_ch_conf.Channel ADC_CHANNEL_0; //输入通道
adc_ch_conf.Rank ADC_REGULAR_RANK_1; //转换序列
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间
HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_1; //输入通道
adc_ch_conf.Rank ADC_REGULAR_RANK_2; //转换序列
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间
HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_2; //输入通道
adc_ch_conf.Rank ADC_REGULAR_RANK_3; //转换序列
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间
HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_3; //输入通道
adc_ch_conf.Rank ADC_REGULAR_RANK_4; //转换序列
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间
HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_4; //输入通道
adc_ch_conf.Rank ADC_REGULAR_RANK_5; //转换序列
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间
HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置adc_ch_conf.Channel ADC_CHANNEL_5; //输入通道
adc_ch_conf.Rank ADC_REGULAR_RANK_6; //转换序列
adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间
HAL_ADC_ConfigChannel(g_adc_nch_dma_handle, adc_ch_conf); //通道配置ADC MSP初始化函数 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();__HAL_RCC_ADC1_CLK_ENABLE();//配置工作模式gpio_init_struct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;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时钟为72M/612MHz HAL_RCCEx_PeriphCLKConfig(adc_clk_init); //设置ADC时钟}
}使能一次ADC DMA传输函数 void adc_dma_enable(uint16_t cndtr)
{ADC1-CR2 ~(1 0); //CONT位置1,关闭连续转换模式DMA1_Channel1-CCR ~(1 0); //禁用DMA通道1while(DMA1_Channel1-CCR (1 0)); //等待DMA通道1禁用完成DMA1_Channel1-CNDTR cndtr; //设置DMA传输数据量DMA1_Channel1-CCR | 1 0; //启用DMA通道1ADC1-CR2 | 1 0; //开启ADC1ADC1-CR2 | 1 22; //启用ADC1的DMA请求
}ADC DMA采集中断服务函数 void DMA1_Channel1_IRQHandler(void)
{if(DMA1-ISR (11)){g_adc_dma_sta 1;//清除中断标志位DMA1-IFCR | (1 1);}
}主函数 int main(void)
{uint16_t i,j;uint16_t adcx;uint32_t sum;float temp;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */adc_nch_dma_init((uint32_t)g_adc_dma_buf); /* 初始化ADC DMA采集 */lcd_show_string(30, 50, 200, 16, 16, STM32, RED);lcd_show_string(30, 70, 200, 16, 16, ADC 6CH DMA TEST, RED);lcd_show_string(30, 90, 200, 16, 16, ATOMALIENTEK, RED);lcd_show_string(30, 110, 200, 12, 12, ADC1_CH0_VAL:, BLUE);lcd_show_string(30, 122, 200, 12, 12, ADC1_CH0_VOL:0.000V, BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 140, 200, 12, 12, ADC1_CH1_VAL:, BLUE);lcd_show_string(30, 152, 200, 12, 12, ADC1_CH1_VOL:0.000V, BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 170, 200, 12, 12, ADC1_CH2_VAL:, BLUE);lcd_show_string(30, 182, 200, 12, 12, ADC1_CH2_VOL:0.000V, BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 200, 200, 12, 12, ADC1_CH3_VAL:, BLUE);lcd_show_string(30, 212, 200, 12, 12, ADC1_CH3_VOL:0.000V, BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 230, 200, 12, 12, ADC1_CH4_VAL:, BLUE);lcd_show_string(30, 242, 200, 12, 12, ADC1_CH4_VOL:0.000V, BLUE); /* 先在固定位置显示小数点 */lcd_show_string(30, 260, 200, 12, 12, ADC1_CH5_VAL:, BLUE);lcd_show_string(30, 272, 200, 12, 12, ADC1_CH5_VOL:0.000V, BLUE); /* 先在固定位置显示小数点 */adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动ADC DMA采集 */while (1){if (g_adc_dma_sta 1){/* 循环显示通道0~通道5的结果 */for(j 0; j 6; j) /* 遍历6个通道 */{sum 0; /* 清零 */for (i 0; i ADC_DMA_BUF_SIZE / 6; i) /* 每个通道采集了50次数据,进行50次累加 */{sum g_adc_dma_buf[(6 * i) j]; /* 相同通道的转换数据累加 */}adcx sum / (ADC_DMA_BUF_SIZE / 6); /* 取平均值 *//* 显示结果 */lcd_show_xnum(108, 110 (j * 30), adcx, 4, 12, 0, BLUE); /* 显示ADC采样后的原始值 */temp (float)adcx * (3.3 / 4096); /* 获取计算后的带小数的实际电压值比如3.1111 */adcx temp; /* 赋值整数部分给adcx变量因为adcx为u16整形 */lcd_show_xnum(108, 122 (j * 30), adcx, 1, 12, 0, BLUE); /* 显示电压值的整数部分3.1111的话这里就是显示3 */temp - adcx; /* 把已经显示的整数部分去掉留下小数部分比如3.1111-30.1111 */temp * 1000; /* 小数部分乘以1000例如0.1111就转换为111.1相当于保留三位小数。 */lcd_show_xnum(120, 122 (j * 30), temp, 3, 12, 0X80, BLUE);/* 显示小数部分前面转换为了整形显示这里显示的就是111. */}g_adc_dma_sta 0; /* 清除DMA采集完成状态标志 */adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动下一次ADC DMA采集 */}LED0_TOGGLE();delay_ms(100);}
}6. 单通道ADC过采样实验 功能使用ADC1通道1(PA1)通过软件方式实现16位分辨率采集外部电压并在LCD模块上面显示对应的ADC转换值以及换算成电压后的电压值。可以使用杜邦线连接PA1到你想测量的电压源(0~3.3V)然后通过TFTLCD显示的电压值。LED0闪烁提示程序运行。 单通道ADC初始化函数 void adc_dma_init(uint32_t mar)
{ADC_ChannelConfTypeDef adc_ch_conf {0};__HAL_RCC_DMA1_CLK_ENABLE(); //使能DMA1时钟g_dma_adc_handle.Instance DMA1_Channel1; //初始化DMA1的通道1g_dma_adc_handle.Init.Direction DMA_PERIPH_TO_MEMORY; //数据传输方向设置为外设到内存g_dma_adc_handle.Init.PeriphInc DMA_PINC_DISABLE; //外设地址不递增g_dma_adc_handle.Init.MemInc DMA_MINC_ENABLE; //内存地址递增g_dma_adc_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_HALFWORD; //数据对齐使用半字g_dma_adc_handle.Init.MemDataAlignment DMA_MDATAALIGN_HALFWORD; //数据对齐使用半字g_dma_adc_handle.Init.Mode DMA_NORMAL; //传输模式配置为正常模式g_dma_adc_handle.Init.Priority DMA_PRIORITY_MEDIUM; //优先级设置为中等HAL_DMA_Init(g_dma_adc_handle);//2.将DMA和ADC句柄连接起来__HAL_LINKDMA(g_adc_dma_handle, DMA_Handle, g_dma_adc_handle);g_adc_dma_handle.Instance ADC1; //选择ADC1g_adc_dma_handle.Init.DataAlign ADC_DATAALIGN_RIGHT; //数据对齐方式为右对齐g_adc_dma_handle.Init.ScanConvMode ADC_SCAN_DISABLE; //非扫描模式仅使用一个通道g_adc_dma_handle.Init.ContinuousConvMode ENABLE; //打开连续转换模式g_adc_dma_handle.Init.NbrOfConversion 1; //通道数选择1个g_adc_dma_handle.Init.DiscontinuousConvMode DISABLE; //禁止规则通道组间断模式 g_adc_dma_handle.Init.NbrOfDiscConversion 0; //配置间断模式的规则通道个数g_adc_dma_handle.Init.ExternalTrigConv ADC_SOFTWARE_START; //软件触发方式HAL_ADC_Init(g_adc_dma_handle); //进行初始化HAL_ADCEx_Calibration_Start(g_adc_dma_handle); //校准ADCadc_ch_conf.Channel ADC_CHANNEL_1; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_1; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_1CYCLE_5; //采样时间HAL_ADC_ConfigChannel(g_adc_dma_handle, adc_ch_conf); //通道配置HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);HAL_DMA_Start_IT(g_dma_adc_handle, (uint32_t)ADC1-DR, mar, 0);HAL_ADC_Start_DMA(g_adc_dma_handle, mar, 0);
}与前面的实验相比此函数只需要修改采样时间即可 adc_ch_conf.SamplingTime ADC_SAMPLETIME_1CYCLE_5;主函数循环代码 while (1){if (g_adc_dma_sta 1){/* 计算DMA 采集到的ADC数据的平均值 */sum 0;for (i 0; i ADC_DMA_BUF_SIZE; i) /* 累加 */{sum g_adc_dma_buf[i];}adcx sum / (ADC_DMA_BUF_SIZE / ADC_OVERSAMPLE_TIMES); /* 取平均值 */adcx 4; /* 除以2^4倍, 得到124位 ADC精度值, 注意: 提高 N bit精度, 需要 N *//* 显示结果 */lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE); /* 显示ADC采样后的原始值 */temp (float)adcx * (3.3 / 65536); /* 获取计算后的带小数的实际电压值比如3.1111 */adcx temp; /* 赋值整数部分给adcx变量因为adcx为u16整形 */lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE); /* 显示电压值的整数部分3.1111的话这里就是显示3 */temp - adcx; /* 把已经显示的整数部分去掉留下小数部分比如3.1111-30.1111 */temp * 1000; /* 小数部分乘以1000例如0.1111就转换为111.1相当于保留三位小数。 */lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE); /* 显示小数部分前面转换为了整形显示这里显示的就是111. */g_adc_dma_sta 0; /* 清除DMA采集完成状态标志 */adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动下一次ADC DMA采集 */}LED0_TOGGLE();delay_ms(100);}在主函数的循环代码中 通过增加采样次数和进行移位操作提高了ADC采样精度。 提高精度的原理 增加采样次数 通过增加采样次数来进行过采样。过采样可以降低噪声提高信噪比从而提高分辨率。将多个采样值相加并取平均值可以增加有效位数。 移位操作 通过移位操作将结果右移以提高分辨率。增加N位精度需要对累加后的结果右移N位。
7. 内部温度传感器实验 功能 通过ADC的通道16读取STM32F103内部温度传感器的电压值并将其转换为温度值显示在TFTLCD屏上。LED0闪烁用于提示程序正在运行。 温度计算方法 ADC内部温度传感器初始化函数 void adc_temperature_init(void)
{ADC_ChannelConfTypeDef adc_ch_conf;g_adc_handle.Instance ADC1; //选择ADC1g_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); //校准ADCadc_ch_conf.Channel ADC_CHANNEL_16; //输入通道adc_ch_conf.Rank ADC_REGULAR_RANK_1; //转换序列adc_ch_conf.SamplingTime ADC_SAMPLETIME_239CYCLES_5; //采样时间HAL_ADC_ConfigChannel(g_adc_handle, adc_ch_conf); //通道配置
}ADC MSP初始化函数 void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{if(hadc-Instance ADC1){RCC_PeriphCLKInitTypeDef adc_clk_init {0};//使能时钟__HAL_RCC_ADC1_CLK_ENABLE();adc_clk_init.PeriphClockSelection RCC_PERIPHCLK_ADC; //ADC外设时钟adc_clk_init.AdcClockSelection RCC_ADCPCLK2_DIV6; //分频因子6时钟为72M/612MHz HAL_RCCEx_PeriphCLKConfig(adc_clk_init); //设置ADC时钟}
}使能一次ADC DMA传输函数 uint32_t adc_get_result()
{//4.启动A/D转换HAL_ADC_Start(g_adc_handle); //开启ADC//5.等待规则通道转换完成HAL_ADC_PollForConversion(g_adc_handle, 10); //轮询转换//6.获取规则通道A/D转换结果return (uint16_t)HAL_ADC_GetValue(g_adc_handle); //返回最近一次规则组的转换结果
}获取内部温度传感器的结果函数 short adc_get_temperature(void)
{uint32_t adcx;adcx adc_get_result();short result;double temperature;temperature adcx * (3.3 / 4096);temperature (1.43 - temperature) / 0.0043 25;result temperature * 100;return result;
}主函数 int main(void)
{short temp;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */adc_temperature_init(); /* 初始化ADC内部温度传感器采集 */lcd_show_string(30, 50, 200, 16, 16, STM32, RED);lcd_show_string(30, 70, 200, 16, 16, Temperature TEST, RED);lcd_show_string(30, 90, 200, 16, 16, ATOMALIENTEK, RED);lcd_show_string(30, 120, 200, 16, 16, TEMPERATE: 00.00C, BLUE);while (1){temp adc_get_temperature(); /* 得到温度值 */if (temp 0){temp -temp;lcd_show_string(30 10 * 8, 120, 16, 16, 16, -, BLUE); /* 显示负号 */}else{lcd_show_string(30 10 * 8, 120, 16, 16, 16, , BLUE); /* 无符号 */}lcd_show_xnum(30 11 * 8, 120, temp / 100, 2, 16, 0, BLUE); /* 显示整数部分 */lcd_show_xnum(30 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE); /* 显示小数部分 */LED0_TOGGLE(); /* LED0闪烁,提示程序运行 */delay_ms(250);}
}8. 光敏传感器实验 声明资料来源战舰STM32F103ZET6开发板资源包 Cortex-M3权威指南(中文).pdfSTM32F10xxx参考手册_V10中文版.pdfSTM32F103 战舰开发指南V1.3.pdfSTM32F103ZET6中文版.pdf战舰V4 硬件参考手册_V1.0.pdf