历史上的今天
返回首页

历史上的今天

今天是:2024年09月27日(星期五)

正在发生

2019年09月27日 | STM32之3路ADC同步转换

2019-09-27 来源:eefocus

初识ADC有一种茫然的状态同时又会觉得它的功能强大,在stm32中有3个ADC,每个ADC都有16个转换通道,由此可知它的强大。首先通过它的引脚功能图可以看出每个引脚所对应的ADC转换通道。这样在编写程序的时候就不会把通道搞错。

在这里插入图片描述

有了上述的了解之后呢,还要记住ADC所能检测的电压范围是0~3.3V,如果检测的电压值大于3.3V就会把ADC烧坏,还有就是ADC转换的时钟频率,在72M的主控CPU中它的转换频率不能大于14M也就是6分频否则检测的值会不准确。采样周期为1.5个时钟周期。stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。当然本次学习是基于规则转换的,库函数操作。有了以上的了解之后来看看ADC的库函数:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对于一些基本的库函数就不列出来了,具体参考stm32固件库使用手册。这里来讲一下几个重要的库函数,第一个就是ADC_Init()在这个函数里有以下参数

在这里插入图片描述

其中ADC_Mode又包含了10种模式

在这里插入图片描述

ADC_ExternalTrigConv 函数里有多种触发方式,一般常用软件触发。

在这里插入图片描述
在这里插入图片描述

这里要注意ADC转换的数据对齐方式,因为stm32的ADC是12位的,而它的数据寄存器ADC_DR是16位的。至于是左对齐还是右对齐?在读取ADC的数据时是从高位开始的,所以一般右对齐就好。

ADC_SampleTime设定了选中通道的ADC 采样时间。 

在这里插入图片描述

采样的周期越长准确度越高,一般设为239.5个周期。ADC_ExternalTrigInjectedConv 指定了所使用的注入转换启动触发。

一般使用软件触发。

在这里插入图片描述

转换的数据被存在了ADC_DR寄存器中,当多通道转换时后一次的数据会把前一次的覆盖所以要用到DMA进行数据处理。以下是DMA的库函数

在这里插入图片描述

重点理解DMA_Init,以下是它的参数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关于DMA_PeripheralBaseAddr该参数用以定义DMA 外设基地址。DMA_MemoryBaseAddr该参数用以定义DMA 内存基地址。这两个函数等会程序里会有说明。下面是库函数配置的步骤:

1 定义ADC、GPIO结构体

2 使能相关时钟,设置分频,IO引脚设为模拟输入

3 复位ADC,选择ADC外设,设定ADC工作模式

4 设置数据对齐方式,通道转换数目

5 使能指定ADC,ADC校准

6 设置转换通道模式,设置ADC通道,采样周期

7 DMA初始化


上程序:


#include "sys.h"

#include "ADC.h"

#define ADC1_2_DR_ADDRESS    ((u32)0x40012400+0x4c)

#define ADC3_DR_ADDRESS    ((u32)0x40013c00+0x4c)

#define NUM 100

__IO u32 ADC1_ConvertedValue[NUM];//防止数据传输溢出造成的错误

__IO u16 ADC3_ConvertedValue[NUM];

void adc_Init(void)

{

//定义结构体

ADC_InitTypeDef ADC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2|RCC_APB2Periph_ADC3, ENABLE);//使能所用功能时钟

RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHZ

  RCC->APB2ENR|=1<<2;                      

  GPIOA->CRH&=0XFFFFF000;//PA0~2上拉输入

RCC->APB2ENR|=1<<9;      //使能ADC时钟

 

//ADC_DeInit(ADC1);

ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult ;//ADC双重模式,同步规则转换 ADC1->CR1|=6<<16 双重模式

ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道转换 

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC转换 ADC1->CR2|=7<<16

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐

ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC 通道的数目

ADC_Init(ADC1, &ADC_InitStructure);//初始化ADC结构体

ADC_Cmd(ADC1, ENABLE);


//ADC_DeInit(ADC2);

ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult ;//ADC双重模式,同步规则转换

ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道转换

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换h

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC转换 ADC2->CR2|=7<<16

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐

ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC 通道的数目

ADC_Init(ADC2, &ADC_InitStructure);//初始化ADC结构体

ADC_Cmd(ADC2, ENABLE);

//ADC_DeInit(ADC3);

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC3独立模式

ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道转换

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC转换

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐

ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC 通道的数目

ADC_Init(ADC3, &ADC_InitStructure);//初始化ADC结构体

ADC_Cmd(ADC3, ENABLE);

//ADC校准

ADC_ResetCalibration(ADC1);//重置指定的ADC 的校准寄存器

ADC_ResetCalibration(ADC2);

ADC_ResetCalibration(ADC3);

while(ADC_GetResetCalibrationStatus(ADC1)&&ADC_GetResetCalibrationStatus(ADC2)&&ADC_GetResetCalibrationStatus(ADC3));//获取ADC 重置校准寄存器的状态

ADC_StartCalibration(ADC1);//开始指定ADC 的校准状态

ADC_StartCalibration(ADC2);

ADC_StartCalibration(ADC3);

while(ADC_GetCalibrationStatus(ADC1)&&ADC_GetCalibrationStatus(ADC2)&&ADC_GetCalibrationStatus(ADC3));//获取指定ADC 的校准程序

//设置指定ADC 的规则组通道,设置它们的转化顺序和采样时间

ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1,ADC_SampleTime_239Cycles5);

ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1,ADC_SampleTime_239Cycles5);

ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1,ADC_SampleTime_239Cycles5);

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)&&ADC_GetFlagStatus(ADC2, ADC_FLAG_EOC)&&ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC));//等待转换结果   

//使能指定的ADC 的软件转换启动功

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

ADC_SoftwareStartConvCmd(ADC2, ENABLE); 

ADC_SoftwareStartConvCmd(ADC3, ENABLE);

}


DMA初始化:


void DMA_InIt(void)

{

DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1|RCC_AHBPeriph_DMA2, ENABLE);


DMA_DeInit(DMA1_Channel1);//DMA1 ADC1通道


DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_2_DR_ADDRESS;

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC1_ConvertedValue;//存放地址

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源 DST则为目的地

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设寄存器地址不递增

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存寄存器地址递增

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//数据宽度为16 位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//数据宽度为16 位

DMA_InitStructure.DMA_BufferSize =  NUM;// 传输数据量的大小

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式

DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

DMA_Init(DMA1_Channel1, &DMA_InitStructure);


DMA_DeInit(DMA2_Channel5);//DMA2 ADC3通道


DMA_InitStructure.DMA_PeripheralBaseAddr = ADC3_DR_ADDRESS;

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC3_ConvertedValue;//存放地址

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源 DST则为目的地

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设寄存器地址不递增

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存寄存器地址递增

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16 位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为16 位

DMA_InitStructure.DMA_BufferSize =  NUM;// 传输数据量的大小

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

DMA_Init(DMA2_Channel5, &DMA_InitStructure);


DMA_Cmd(DMA1_Channel1, ENABLE);//使能或者失能指定的通道

DMA_Cmd(DMA2_Channel5, ENABLE);

//使能ADC的DMA请求

ADC_DMACmd(ADC1, ENABLE);

  ADC_DMACmd(ADC2, ENABLE);

ADC_DMACmd(ADC3, ENABLE);

}


//转换值传出:

float v_avr = 0;

void Get_Average(void)

{

u16 i = 0;

u32 adc1_sum = 0;

for(i=0; i {

adc1_sum += ADC1_ConvertedValue[i]%65536;

}

v_avr = adc1_sum/NUM/4096.0*3300;

}


由于在学习中只用到了一个IO用于检测电压,故只写了一个传出值。


*问题:DMA的地址怎么选?*如何快速的找到这个寄存器的地址,首先打开参考手册,移动到2.3节—寄存器映像,也就是第28页,这里列出了STM32内部地址的划分,找到你要的外设基地址,例如ADC1的基地址为0x4001 2400 。然后再你需要的外设关于寄存器的地方找到寄存器的偏移地址,如ADC1的DR寄存器的偏移地址为0x4c,基地址加偏移地址就得到这个寄存器的地址了。


C语言知识:定义extern变量后不能赋以初值,否则会报错。外部变量在编译时赋值,只赋值一次。


总结:之前写的程序逻辑混乱,事件发生的顺序被颠倒导致转换出现问题,一定要仔细检查!理清步骤!

推荐阅读

史海拾趣

Cotco公司的发展小趣事

在电子行业快速发展的浪潮中,Cotco公司于XXXX年正式成立,由几位在电子行业有着丰富经验的企业家和技术专家共同创立。初期,公司专注于电子元器件的分销和贸易,凭借创始团队成员在行业内的人脉和资源,Cotco很快与多家知名电子元器件制造商建立了合作关系,开始为客户提供优质的元器件采购服务。随着市场需求的不断增长,Cotco的业务也逐渐扩展,开始涉足电子产品的设计和生产领域。

Herth+Buss Fahrzeugteile GmbH & Co KG公司的发展小趣事
安装在门窗、围墙等处,提高家庭安防水平。
富之光(Fujicon)公司的发展小趣事

为了寻求更广阔的发展空间,富致科技积极参与各类产业交流活动。例如,在2023年7月,富致科技的董事长陈继圣和总厂长叶敬强前往中国大陆芜湖市进行考察交流,参观了奇瑞新能源汽车股份有限公司等企业,并与芜湖宏景电子、安瑞光电、致通汽车电子等产业链相关企业进行了深入交流。这些交流不仅让富致科技更加了解大陆市场的需求和潜力,也为双方未来的合作奠定了良好的基础。

Frequency Electronics Inc公司的发展小趣事

高频电子深知技术创新是企业持续发展的核心动力。因此,公司一直保持着对研发的持续投入,不断推出新产品、新技术和新服务。同时,高频电子还积极与高校、科研机构等合作,共同开展前沿技术的研发和应用。这些努力使得高频电子在技术创新方面始终保持着领先地位,为公司的长期发展奠定了坚实的基础。

综上所述,高频电子通过战略转型、技术创新、全球化布局、多元化业务布局以及持续投入研发等举措,实现了从一家小型国防合同制造商到全球领先的高科技供应商的华丽蜕变。这些故事不仅展现了高频电子的发展历程和成就,也为我们揭示了企业成功背后的关键因素。

EDSYN公司的发展小趣事

为了帮助客户更便捷地将设备连接到物联网,Econais与Xively展开了合作。通过这一合作,Econais的WiSmart Wi-Fi用户可以无缝连接到Xively云,为工业、商业和住宅市场带来了创新的医疗保健、智能能源、无线音频控制和监控产品。这种一站式的物联网解决方案大大加快了物联网产品的上市时间,同时也降低了产品的开发成本。

Greenwich Instruments Ltd公司的发展小趣事

面对日益激烈的市场竞争和不断变化的客户需求,Greenconn Corp积极推动智能制造转型。公司引进先进的生产设备和技术,优化生产流程和管理模式,提高生产效率和产品质量。同时,公司还加强与高校、科研机构的合作,共同研发智能制造关键技术,推动智能制造技术在连接器生产领域的应用。这些努力不仅提升了Greenconn Corp的竞争力,还为公司的可持续发展奠定了坚实基础。

问答坊 | AI 解惑

模拟电子电路

大家一起共享!…

查看全部问答>

帮忙推荐个控制可控硅的专用IC

1.COMS工艺,微功耗器件, 2.包含电压过零检测功能, 3.输出驱动能力大, 4.输出是脉冲波并且脉宽可调, (检测到有触发信号后,检测到过零点时IC输出一脉冲让SCR/triac导通) 5.最好还有延时功能的,并且延时时间可调, (延时期间,IC每检测到过0点电压 ...…

查看全部问答>

【藏书阁】单片机原理及系统设计

目录: 第1章微型计算机基础1 1.1微型计算机数制及其转换1 1.1.1微型计算机的数制1 1.1.2微型计算机数制间数的转换4 1.2微型计算机的二进制数运算7 1.2.1算术运算7 1.2.2逻辑运算10 1.3微型计算机码制和编码11 1.3.1微型计算机中数的表 ...…

查看全部问答>

求帮助

我要设计一个乒乓球游戏机:要求: 1.用8个发光二极管表示球;用两个按钮分别表示甲乙两个球员的球拍;2.一方发球后,球以固定速度向另一方运动(发光二极管依次点亮),当球达到最后一个发光二极管时,对方击球(按下按钮)球将向相反方向运动 ...…

查看全部问答>

有关STM32数据采样

本人嵌入式专业专科生 第一次接触STM32 老师给我们一个题目 基于STM32的数据采样系统   请教各位大侠这题目怎么做?尽量描述的详细些。。。。。非常感谢…

查看全部问答>

wince 下 treeview item 高度设置

我是在sdk下 开发的。 wince 下的TreeView_SetItemHeight 不能用。 custom draw 方法也改不了Item的高度 高人指点一下,有什么好的方法…

查看全部问答>

请大家帮忙!问点汇编编程题目,考试要用,先谢谢了!!!

1、编写子程序,功能为实现求4字节的减法。输入参数为subt1(4bytes),subt2(4bytes),输出参数为right(4bytes)。子程序的名字为subtract。 2. 设置pta4、ptc3、ptd2为输出,其中pta4输出高电平,ptc3、ptd2输出低电平。 3. 设置AD为单次采集,8位 ...…

查看全部问答>

很想帮人家回答问题

每天在这个论坛看帖 很想帮别人解决问题 看了很多 没个我会  艾  什么时候我才\"牛\"呢 没信心了!…

查看全部问答>

学习嵌入式应该用Linux什么版本

新手请教。希望有人仔细说说。…

查看全部问答>

关于抢占优先级和子优先级的疑惑

APINT寄存器里面的PRIGROUP字段可以设置抢占优先级和子优先级的分级参考CM3权威指南 P114的说法:如果只使用3个位来表达优先级([7:5]),并且优先级组的值是5(从比特5处分组),则得到4级抢占优先级,且在每个抢占优先级的内部有2个子优先级有几 ...…

查看全部问答>