当前位置: 首页 > news >正文

江协科技STM32课程笔记(五)— ADC模数转换器

一、ADC简介

stm32F103C8T6有ADC1和ADC2,10个外部输入通道。

逐次逼近型ADC:

左边IN0~IN7是输入端口,通过ADDA、ADDB和ADDC来决定通道选择,ALE为锁存信号。DAC通常us级,所以如果要对多个信号进行转换,不需要设计多个ADC,只需要通过通道地址,将通道选择器开关拨到对应通道即可。

后面是个比较器,上面为待测信号,下面为DAC输出的已知编码的电压值,通过SAR寄存器,二分法的方式逐渐查找电压对应的量化值。比如这里是8位的ADC,那编码就是从0~255,第一次比较的时候,我们就给DAC输入255的一半进行比较,那就是128,然后看谁大谁小,如果DAC电压大了,那第二次比较的时候,给128的一半,64,如果还大,第三次比较的时候就给32,如果这次DAC电压小了,那就给32到64中间的值,依次进行下去,就能最快找到未知电压的编码。128、64、32这些数据,正好是二进制每一位的位权,这个判断过程就相当于是,对二进制高位到低位依次判断是1还是0的过程。对于12位ADC就要判断12次。

ADC结束后,SAR输出到锁存缓冲器。EOC是转换结束信号,START是开始转换,给一个输入脉冲开始转换。CLOCK为时钟。VREF是参考电压,通常等于VCC。

STM32的ADC:

1、首先左边的ADC_IN为输入通道,还有两个内部输入通道温度传感器和Vrefint参考电压。

2、注入通道和规则通道,注入通道最多4通道,规则通道最多16通道。规则组可以转换16个通道,但是因为数据寄存器16位只有一个,所以后面来的通道数据会覆盖前一个的数据,通常结合DMA来使用,将数据挪到内存中。注入组可以同时存储4个通道数据,不用担心被覆盖。

3、左下角是触发转换信号。STM32触发ADC的信号有两种,软件触发(代码)和硬件触发。硬件触发主要来源于定时器触发,如TRGO主模式输出,用定时器每隔一段时间触发ADC转换,不用定时中断。也可以选择EXTI外部中断引脚触发。

4、

5、ADC时钟来自ADC预分频器。最大支持14MHz,所以分频器只能选6或者8。

6、模拟看门狗。可以存上下限阈值,如果该通道超过阈值,就会产生看门狗中断,通向NVIC的ADC中断。

7、EOC是规则组完成信号;JEOC是注入组完成信号;如果NVIC开启了相关通道,他们俩也能产生中断。

STM32F103C8的ADC:

ADC1和ADC2输入通道引脚相同,用于双ADC交叉模式,提高采样频率。STM32F103C8只有通道0到9

规则组的4种转换模式:

单次转换,非扫描模式:一次转一个通道,转换前修改通道,等待下一次触发。

连续转换,非扫描模式:一次转一个,但是一次转换后立刻开始下一次转换

单次转换、扫描模式:一次转多个通道,每次触发后等待下一次触发。转换完成后需要通过DMA及时保存寄存器中的结果。

连续转换、扫描模式:

还有一个间断扫描模式,可以每隔几个转换就暂停一次,需要等待触发再次转换。

触发控制:

规则组触发源:

注入组触发源:

数据对齐:

ADC为12位的,但是数据寄存器是16位的,存在数据对齐问题。一般采用右对齐,直接读取寄存器即可。左对齐用于可以读取寄存器只取高位,如高8位,舍弃低4位精度,12位ADC退化为8位ADC。

转换时间:

AD步骤采样、保持、量化和编码。由于AD转换需要一定时间,所以需要保持输入电压不变,通过一个采样开关控制,可以通过一个电容来实现,那这个采样并保持的过程就需要时间。如果提高ADCCLK提高了,ADC处于超频状态,但是稳定性不能保证。

校准:

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。建议在每次上电后执行一次校准。启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期。对于我们来说就是需要初始化后利用代码调用自校准。

二、AD单通道

/*RCC库函数里*/
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);    // 配置ADCCLK分频器的,可以对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK/*ADC库函数里*/
void ADC_DeInit(ADC_TypeDef* ADCx);        // 恢复缺省配置
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);   // 初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);                // 结构体初始化
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);           // 用于给ADC上电,也就是开关控制
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);        // 用于开启DMA输出信号,若果使用DMA转运数据,就得调用这个函数
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);   // 中断输出控制,用于控制某个中断,能不能通往NVIC
/*下面4个函数是用于获取控制校准的函数、在ADC初始化完成之后,一次调用就可以了*/
void ADC_ResetCalibration(ADC_TypeDef* ADCx);                        // 复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);         // 获取复位校准状态
void ADC_StartCalibration(ADC_TypeDef* ADCx);                        // 开始校准
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);              // 获取开始校准状态
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);    
// ADC_软件开始转换控制,这个就是用于软件触发的函数了,调用一下,就能软件触发转换了
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx); 
//ADC获取软件开始转换状态,给SWSTART位置1,以开始转换,这个函数一般不用
/*下面2个函数是用来配置ADC间断模式的*/
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);//每隔几个通道间断一次
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);   //是不是启用间断模式
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
/*ADC规则组通道配置,很重要,作用就是给序列的每个位置填写指定的通道
ADC_Channel:要指定的通道;
Rank:序列几的位置;
ADC_SampleTime:指定通道的采样时间。
*/
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//ADC外部触发转换控制,就是是否允许外部触发转换
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);  // ADC获取转换值,获取ADC转换的数据寄存器,读取转换结果就是要用这个函数
uint32_t ADC_GetDualModeConversionValue(void);//ADC获取双模式转换通道,这个是双ADC模式读取转换结果的函数,暂时不用
/*下面带Injected的函数都是对ADC注入组进行配置的*/
void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv);
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length);
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset);
uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);
/*下面三个函数是对模拟看门狗进行配置的*/ 
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog); //是否启动模拟看门狗
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);   //配置高低阈值
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);  //配置看门的通道
void ADC_TempSensorVrefintCmd(FunctionalState NewState);//ADC温度传感器、内部参考电压控制,用来开启内部的两个通道的
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);   
//获取标志位状态,参数给EOC标志位,就可以判断EOC标志位是不是置1了,如果转换结束,EOC标志位置1
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);  //清除标志位
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);    //获取中断状态
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);  //清除中断挂起位

三、AD多通道

江协科技是规则组一个一个转换,自己尝试改的4通道注入组的

void AD_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*设置ADC时钟*/RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入ADC_InjectedSequencerLengthConfig(ADC1,4);ADC_InjectedChannelConfig(ADC1, ADC_Channel_0 , 1, ADC_SampleTime_55Cycles5);ADC_InjectedChannelConfig(ADC1, ADC_Channel_1 , 2, ADC_SampleTime_55Cycles5);ADC_InjectedChannelConfig(ADC1, ADC_Channel_2 , 3, ADC_SampleTime_55Cycles5);ADC_InjectedChannelConfig(ADC1, ADC_Channel_3 , 4, ADC_SampleTime_55Cycles5);/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止ADC_InitStructure.ADC_ScanConvMode = ENABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置ADC_InitStructure.ADC_NbrOfChannel = 4;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1/*ADC使能*/ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行/*ADC校准*/ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
}/*** 函    数:获取AD转换的值* 参    数:无* 返 回 值:AD转换的值,范围:0~4095*/
void AD_GetValue(void)
{ADC_ExternalTrigInjectedConvConfig(ADC1,ADC_ExternalTrigInjecConv_None);ADC_SoftwareStartInjectedConvCmd(ADC1, ENABLE);					//软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC) == RESET);	//等待EOC标志位,即等待AD转换结束ADC_ClearFlag(ADC1,ADC_FLAG_JEOC);	
}int main(void)
{uint16_t AD0,AD1,AD2,AD3;			//定义AD值变量float Voltage;				//定义电压变量OLED_Init();				//LED初始化AD_Init();					//PWM初始化/*显示静态字符串*/OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:");int i;while(1){for(i = 0;i<10;i++){AD_GetValue();					//软件触发AD转换一次AD0 += ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_1);					//获取AD转换的值AD1 += ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_2);AD2 += ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_3);AD3 += ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_4);}OLED_ShowNum(1, 5, AD0/10, 4);				//显示通道0的转换结果AD0OLED_ShowNum(2, 5, AD1/10, 4);				//显示通道1的转换结果AD1OLED_ShowNum(3, 5, AD2/10, 4);				//显示通道2的转换结果AD2OLED_ShowNum(4, 5, AD3/10, 4);				//显示通道3的转换结果AD3AD0 = 0;AD1 = 0;AD2 = 0;AD3 = 0;sysDelayms(300);			//延时100ms,手动增加一些转换的间隔时间}}

连续测量需要DMA,放在下一节

http://www.dtcms.com/a/507242.html

相关文章:

  • 什么是慢查询,慢请求,以及如何避免
  • 网站设计模板简约福州网站设计
  • 各大网站做推广广告什么是企业形象设计
  • 大模型金融量化比赛
  • Kubernetes深入学习之容器入门(一)
  • Docker安装部署MrDoc觅思文档-免费的国产知识库管理系统
  • 批量更新操作全攻略:从JDBC原理到多框架实现(MyBatis/MyBatis-Plus/Nutz)
  • 简述:普瑞时空数据建库软件(国土变更建库)之一(2025年部分新规则)
  • 零基础新手小白快速了解掌握服务集群与自动化运维(十二)Python编程之面向对象
  • 刚学做网站怎么划算普洱专业企业网站建设
  • Java基础——面向对象复习知识点12
  • IPv6路由技术
  • 网站建设开票开什么内容电脑禁止访问网站设置
  • WPeChatGPT 插件使用教程(转载)
  • 从 Sora 到 Sora 2:文本生成视频进入下一个阶段(附sora教程)
  • k8s(十二)Rancher详解
  • 4. 前馈网络(FeedForward):给每个词“做深度加工”
  • wordpress一步步建企业网站上海有名的广告设计公司
  • 百度搜索站长平台汽车网站建设目的
  • EDA--三井物产商品预测挑战赛 Exploratory Data Analysis(探索性数据分析)
  • 【云计算专题会议】第二届云计算与大数据国际学术会议(ICCBD 2025)
  • AI CRM中的数据分析:悟空AI CRM如何帮助企业优化运营
  • Git多项目提交记录提取与数据分析指南
  • 网站后台账号密码忘记了怎么办漳平网络建站公司
  • 响水做网站价格上海网站设计成功柚v米科技
  • Elasticsearch面试精讲 Day 26:集群部署与配置最佳实践
  • 搭建Jenkins
  • 多语言NLP数据处理:核心环节与实践要点
  • 无法远程连接 MySQL
  • 域名seo站长工具中文网址大全2345