layout: post

tags: [STM32]

comments: true

什么是ADC?

Analog to Digital Converter,将模拟信号转换成数字的模数转换器,后面可能还会接触到DAC,恰恰相反,是将数字信号转换成模拟信号。具体的原理可以自行找搜索引擎,可以得到更好的答案。

STM32 ADC的特性

参考手册给出ADC的功能十分丰富,具体如下:

  • 12 bit分辨率,量化到0-4096的范围;
  • 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
  • 单次和连续转换模式
  • 从通道0到通道n的自动扫描模式
  • 自校准
  • 带内嵌数据一致性的数据对齐
  • 采样间隔可以按通道分别编程
  • 规则转换和注入转换均有外部触发选项
  • 间断模式

    本文只讨论规则采样和注入采样,并给出具体的代码实现,更多细节还需要参考《STM32参考手册》

采样模式

  • 规则采样:相当于软件触发采样,可以在程序里主动调用规则采样去读取具体的ADC值,同样
  • 注入采样:相当于中断,所以需要具体的触发源,比如外部的信号可以触发注入采样,ADC转换成功之后,便会触发ADC中断,在中断服务子程序中,就可以读取ADC值;

触发源可以是外部信号,也可以是定时器的触发信号;标准库中注入模式的触发信号如下所示;



注入组的外部触发信号

/** @defgroup ADC_external_trigger_sources_for_injected_channels_conversion
* @{
*/ #define ADC_ExternalTrigInjecConv_T2_TRGO ((uint32_t)0x00002000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T2_CC1 ((uint32_t)0x00003000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T3_CC4 ((uint32_t)0x00004000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T4_TRGO ((uint32_t)0x00005000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4 ((uint32_t)0x00006000) /*!< For ADC1 and ADC2 */ #define ADC_ExternalTrigInjecConv_T1_TRGO ((uint32_t)0x00000000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigInjecConv_T1_CC4 ((uint32_t)0x00001000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigInjecConv_None ((uint32_t)0x00007000) /*!< For ADC1, ADC2 and ADC3 */ #define ADC_ExternalTrigInjecConv_T4_CC3 ((uint32_t)0x00002000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T8_CC2 ((uint32_t)0x00003000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T8_CC4 ((uint32_t)0x00004000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T5_TRGO ((uint32_t)0x00005000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T5_CC4 ((uint32_t)0x00006000) /*!< For ADC3 only */

规则组的外部触发信号

#define ADC_ExternalTrigConv_T1_CC1                ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 */ #define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 */ #define ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) /*!< For ADC3 only */

从参考手册中可以看到ADC的框架,注入通道转换成功之后,标志位JEOC使能,然后JEOCIE中断位被使能,最终触发ADC中断;

采样时间

标准库中对于采样周期的定义;

#define ADC_SampleTime_1Cycles5                    ((uint8_t)0x00)
#define ADC_SampleTime_7Cycles5 ((uint8_t)0x01)
#define ADC_SampleTime_13Cycles5 ((uint8_t)0x02)
#define ADC_SampleTime_28Cycles5 ((uint8_t)0x03)
#define ADC_SampleTime_41Cycles5 ((uint8_t)0x04)
#define ADC_SampleTime_55Cycles5 ((uint8_t)0x05)
#define ADC_SampleTime_71Cycles5 ((uint8_t)0x06)
#define ADC_SampleTime_239Cycles5 ((uint8_t)0x07)

TCONV = 采样时间+ 12.5个周期

ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);

ADCCLK=14MHz,采样时间为1.5周期(ADC_SampleTime_1Cycles5)

采样时间TCONV = 1.5 + 12.5 = 14周期 = 1μs

代码实现

基于ST标准库V3.5,实现了ADC规则采样和注入采样两种模式;

current.h

#ifndef CURRENT_H
#define CURRENT_H
#include <stdint.h> #define RES_IA 1024 //采样电阻A
#define RES_IB 1024 //采样电阻B /**
* Ia--->PA0/ADC0
* |------------>PA2/ADC2
* Ib--->PA1/ADC1
* |------------>PA3/ADC3
*/ void cur_fbk_init(void); uint16_t cur_fbk_get_Ia(void);
uint16_t cur_fbk_get_Ia_avl(uint8_t sample_times);
uint16_t cur_fbk_get_Ib(void);
uint16_t cur_fbk_get_Ib_avl(uint8_t sample_times); uint16_t get_inject_ia(void);
uint16_t get_inject_ib(void); int16_t cur_fbk_get_theta(void); #endif

current.c

#include "current.h"
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_rcc.h"
#include <stdint.h> #if 1
#define IA_CHANNEL_DRI ADC_Channel_0
#define IB_CHANNEL_DRI ADC_Channel_1
#else
#define IA_CHANNEL_DRI ADC_Channel_2
#define IB_CHANNEL_DRI ADC_Channel_3
#endif volatile uint16_t Ia_val = 0;
volatile uint16_t Ib_val = 0; static void cur_fbk_irq_init(void){ NVIC_InitTypeDef NVIC_InitStructure; /* Configure and enable ADC interrupt */
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); } static void cur_fbk_adc_init(void){ ADC_DeInit(ADC1);
ADC_InitTypeDef ADC_InitStructure;
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2; /* Set injected sequencer length */
ADC_InjectedSequencerLengthConfig(ADC1, 2);
/* ADC1 injected channel Configuration */
ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_71Cycles5);
//ADC_InjectedChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_71Cycles5);
//ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_71Cycles5);
/* ADC1 injected external trigger configuration */
//ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_CC4);
ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_None); //ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_1,2048);
//ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_2,2048); /* Enable automatic injected conversion start after regular one */
ADC_AutoInjectedConvCmd(ADC1, ENABLE); /* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 external trigger */
ADC_ExternalTrigConvCmd(ADC1, ENABLE); ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)){
/**
TODO
timeout_detect
*/ }
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)){
/**
TODO
timeout_detect
*/
} ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);
} static void cur_fbk_rcc_init(void){ /* ADCCLK = PCLK2/6 */
RCC_ADCCLKConfig(RCC_PCLK2_Div6); /* Enable peripheral clocks ------------------------------------------------*/
/* Enable DMA1 and DMA2 clocks */
//RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 | RCC_AHBPeriph_DMA2, ENABLE); /* Enable ADC1 and GPIOA clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
} static void cur_fbk_pin_init(void){ GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure); } static uint16_t cur_fbk_get_ad_val(uint8_t channel){
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能软件转换功能
//等待转换结束
/*
EOC:转换结束位 (End of conversion)
该位由硬件在(规则或注入)通道组转换结束时设置,由软件清除或由读取ADC_DR时清除
0:转换未完成;
1:转换完成。
*/
while((ADC1->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC){}
//while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )){} return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果
} void cur_fbk_init(void){
cur_fbk_rcc_init();
cur_fbk_pin_init();
cur_fbk_irq_init();
cur_fbk_adc_init();
} uint16_t cur_fbk_get_Ia_avl(uint8_t sample_times){
uint32_t Ia_sum = 0;
int8_t i;
for(; i < sample_times; i++){
Ia_sum+=cur_fbk_get_Ia();
}
return (uint16_t)(Ia_sum/sample_times);
} uint16_t cur_fbk_get_Ib_avl(uint8_t sample_times){
uint32_t Ib_sum = 0;
int8_t i = 0;
for(;i < sample_times; i++){
Ib_sum+=cur_fbk_get_Ib();
}
return (uint16_t)(Ib_sum/sample_times);
} uint16_t cur_fbk_get_Ia(void){
return cur_fbk_get_ad_val(IA_CHANNEL_DRI);
} uint16_t cur_fbk_get_Ib(void){
return cur_fbk_get_ad_val(IB_CHANNEL_DRI);
} int16_t cur_fbk_get_theta(void){
return 0;
} /******************************************************************************/
/* STM32F10x Peripherals Interrupt Handlers */
/******************************************************************************/ /**
* @brief This function handles ADC1 and ADC2 global interrupts requests.
* @param None
* @retval None
*/
void ADC1_2_IRQHandler(void)
{ Ia_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
Ib_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
/* Clear ADC1 JEOC pending interrupt bit */
ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);
} uint16_t get_inject_ia(void){
return Ia_val;
} uint16_t get_inject_ib(void){
return Ib_val;
}

STM32 ADC多通道规则采样和注入采样的更多相关文章

  1. 【stm32】ADC的规则通道和注入通道混合使用

    之前完成了规则通道DMA的数据传输了,不过平时在使用ADC的时候可能就会遇到很多情况,不可能就这样简单的按规则通道来采样,DMA存储,使用数据的:可能有时候会需要立刻采样,那样我们就需要利用到注入通道 ...

  2. STM32 ADC多通道转换DMA模式与非DMA模式两种方法(HAL库)

    一.非DMA模式(转) 说明:这个是自己刚做的时候百度出来的,不是我自己做出来的,因为感觉有用就保存下来做学习用,原文链接:https://blog.csdn.net/qq_24815615/arti ...

  3. STM32—ADC多通道采集电压

    文章目录 ADC详解 程序说明 函数主体 引脚配置 ADC和DMA配置 主函数 ADC详解 前面的博客中详细介绍了STM32中ADC的相关信息,这篇博客是对ADC内容的一个总结提升,ADC的详细介绍: ...

  4. STM32 ADC多通道转换

    描述:用ADC连续采集11路模拟信号,并由DMA传输到内存.ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ.在每次转换结束后,由DMA循环将转换的数据传输到内存中.ADC可以连续采集N ...

  5. STM32 双ADC同步规则采样

      最近需要用到两个ADC对电压电流进行同步采样,看了一下STM32的ADC介绍,发现STM32最多有3个独立ADC,有在双AD模式下可以进行同步测量,正好满足我的要求.参考官方给的例子在结合自己的需 ...

  6. Hi3518EV200平台ADC多通道采样

    Hi3518EV200平台ADC多通道采样流程 Hi3518EV200 ADC 本文针对Hi3518EV200平台处理器,通过ADC单次采样方式,实现对多通道(1~4通道)ADC进行采样控制.本文仅仅 ...

  7. STM32 ADC 采样 频率的确定

    一 STM32 ADC 采样频率的确定 1.       : 先看一些资料,确定一下ADC 的时钟: (1),由时钟控制器提供的ADCCLK 时钟和PCLK2(APB2 时钟)同步.CLK 控制器为A ...

  8. stm32中adc的常规通道和注入通道的区别

    STM32的每个ADC模块通过内部的模拟多路开关,可以切换到不同的输入通道并进行转换.STM32特别地加入了多种成组转换的模式,可以由程序设置好之后,对多个模拟通道自动地进行逐个地采样转换. 有2种划 ...

  9. STM32—ADC详解

    文章目录 一.ADC简介 二.ADC功能框图讲解 1.电压输入范围 2.输入通道 3.转换顺序 4.触发源 5.转换时间 6.数据寄存器 7.中断 8.电压转换 三.初始化结构体 四.单通道电压采集 ...

随机推荐

  1. BUG 测试计划

       性能追求 目前状况 测试标准 APP平稳运行,无crush现象   快速下拉翻页时,崩溃退出     要求多人使用,均流畅无异常退出方可               页面的放大缩小不会造成页面显 ...

  2. The Super Powers UVA - 11752

    题目大意:将范围从1~pow(2,64)-1内的super power输出.super power的定义:一个数x至少存在两种x=pow(i,k),(k!=1). 题解: 注意数据范围2的64次方-1 ...

  3. 掷骰子 dp

    B. 掷骰子 单点时限: 2.0 sec 内存限制: 512 MB 骰子,中国传统民间娱乐用来投掷的博具,早在战国时期就已经被发明. 现在给你 n 个骰子,求 n 个骰子掷出点数之和为 a 的概率是多 ...

  4. Java环境下 selenium webDriver + chrome浏览器搭建与调试

    一.首先下载selenium webDriver jar包,下载地址如下: http://selenium-release.storage.googleapis.com/index.html 二.下载 ...

  5. 初始化 RESTful API 风格的博客系统

    作者:HelloGitHub-追梦人物 文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 在 HelloDjango 全栈系列教程的第一步--Django博客教程(第二版)中 ...

  6. 2020.4面试分享(7面收割5个offer)

    都说金三银四是找工作的最佳时节,由于本人的个人职业规划跟目前工作内容不太相符(具体原因就不透露了,领导平时也要来这里逛,哈哈),四月份挑选了10多家公司投递简历(公司规模从几十人到上万人都有),参加了 ...

  7. linux上Docker安装gogs私服亲测(详解)

    一.前言 有网友问我为什么要使用私服,可能大部分人都不是太懂,网上那么多存储仓库而且好用方便,但是你想过没有如果企业中的项目,放在人家的仓库上这个安全性不是太好,所以说一般企业都会有自己的私服.本章教 ...

  8. JDBC 进阶:使用封装通用DML DQL 和结构分层以及at com.mysql.jdbc.PreparedStatement.setTimestamp空指针异常解决

    准备: 数据表 CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(10) DEFAULT ...

  9. Python爬虫篇(代理IP)--lizaza.cn

    在做网络爬虫的过程中经常会遇到请求次数过多无法访问的现象,这种情况下就可以使用代理IP来解决.但是网上的代理IP要么收费,要么没有API接口.秉着能省则省的原则,自己创建一个代理IP库. 废话不多说, ...

  10. Windows系统安装最新版本RabbitMQ3.8.3及报错解决

    今天想安装下RabbitMQ写几个用例看下,发现最新的安装包有些问题,不能直接安装使用,遇到一些问题,记录一下解决办法. 下载安装包 因为RabbitMQ是Erlang编写,安装时,需要先安装Erla ...