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

【STM32单片机】#8 定时器编码器接口ADC模数转换器

主要参考学习资料:

B站@江协科技

STM32入门教程-2023版 细致讲解 中文字幕

开发资料下载链接:https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwd=dspb

单片机套装:STM32F103C8T6开发板单片机C6T6核心板 实验板最小系统板套件科协

实验:

  • 编码器接口测速
  • ADC单通道
  • ADC多通道

新函数:

  • 定时器库函数(编码器接口部分)
  • ADC库函数(部分)

目录

  • 编码器接口
    • 编码器接口简介
    • 正交编码器
    • 编码器接口框图
    • 编码器接口基本结构
    • 工作模式
    • 实例
    • 函数详解
      • TIM_EncoderInterfaceConfig函数
    • 实验15 编码器接口测速
      • 接线图
      • 编码器接口驱动
      • 主程序
  • ADC数模转换器
    • ADC简介
      • 逐次逼近型ADC
      • ADC框图
      • ADC基本结构图
      • 输入通道
      • 转换模式
        • 单次转换-非扫描模式
        • 连续转换-非扫描模式
        • 单次转换-扫描模式
        • 连续转换-扫描模式
      • 触发控制
      • 数据对齐
      • 转换时间
      • 校准
    • 硬件电路
    • 函数详解
      • RCC_ADCCLKConfig函数
      • ADC_DeInit函数
      • ADC_Init函数
        • ADC_InitTypeDef结构体
      • ADC_StructInit函数
      • ADC_Cmd函数
      • ADC_DMACmd函数
      • ADC_ITConfig函数
      • 校准函数
    • ADC_SoftwareStartConvCmd函数
      • ADC_GetSoftwareStartConvStatus函数(不常用)
      • ADC_DiscModeChannelCountConfig函数
      • ADC_DiscModeCmd函数
      • ADC_RegularChannelConfig函数
      • ADC_ExternalTrigConvCmd函数
      • ADC_GetConversionValue函数
      • ADC_GetDualModeConversionValue函数
      • 中断标志位函数
    • 实验16 AD单通道
      • 接线图
      • ADC驱动
      • 主程序
      • 连续转换
    • 实验17 AD多通道
      • 接线图
      • ADC驱动
      • 主程序

编码器接口

编码器接口简介

  • Encoder Interface 编码器接口
  • 编码器接口可接受增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度。
  • 每个高级定时器和通用定时器都拥有一个编码器接口。
  • 两个输入引脚借用了输入捕获的通道1和通道2。

正交编码器

正交编码器一般可以测量位置或带有方向的速度值,有A相和B相两个信号输出引脚,在正转和反转情况下输出电平波形如下:

编码器旋转的速度决定方波的频率,方向决定A相和B相方波的相位差。当编码器正转时,A相提前B相90°;当编码器反转时,A相滞后B相90°,因此该对信号被称为正交信号。旋转的方向可以自行定义极性。正交信号的优点是精度更高,A相和B相都可以计次,且可以以两路信号交替跳变为依据判别噪声。

编码器接口框图

编码器接口的两个输入端TI1FP1和TI2FP2借用了输入捕获单元的两个通道,分别接A相和B相,并和输入捕获单元共用输入滤波器和边沿检测器。编码器接口的输出部分通过从模式控制器控制CNT的计数时钟和计数方向,如果出现了边沿信号,且对应另一相的状态为正转,则控制CNT自增,反之CNT自减。此时CNT不会使用内部时钟和时基单元配置的计数方向。

编码器接口基本结构

在编码器接口中,ARR仍然有效,一般设置为65535,以便利用补码的定义得到负数。当CNT从0自减时,下一个数为65535,将其从16位无符号数转换为16位有符号数即可。

工作模式

有效边沿决定了CNT在哪一相的上升沿和下降沿时根据另一相的电平状态执行自增或自减,仅在某一相计数时则忽略另一相的边沿。右侧对应关系与正交编码器介绍中展示的表格等效,正转向上计数,反转向下计数。一般使用双相计数,因为其计数精度更高。

实例

图中信号产生毛刺时,CNT只会自增自减来回摆动,实现抗噪声效果。

由于高低电平对编码器接口都有效,因此输入通道的极性选择不影响其有效性,选上升沿则不取反,选下降沿则取反。通过改变其中一相的极性可以交换正反转的方向。

函数详解

TIM_EncoderInterfaceConfig函数

简介:定时器编码器接口配置。

参数一:定时器名称

参数二:工作模式

TIM_EncoderMode_TI1, TIM_EncoderMode_TI2, TIM_EncoderMode_TI12

参数三:通道1电平极性

TIM_ICPolarity_Falling, TIM_ICPolarity_Rising

参数四:通道2电平极性

实验15 编码器接口测速

接线图

编码器接口驱动

由于博主可能存在硬件问题,即使下了官方代码也未能复现。以下程序全程跟随视频手敲,但效果没有得到验证。

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_Hvoid Encoder_Init(void);
int16_t Encoder_Get(void);#endif

Encoder.c

#include "stm32f10x.h"void Encoder_Init(void)
{//初始化代码沿用实验13RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;//补充说明:上拉/下拉输入的选择原则是外部模块的默认状态输出,二者保持一致//默认高电平比较普遍,因此一般选上拉输入//若不确定默认状态或外部信号输出功率非常小则尽量选择浮空输入GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置PA6和PA7GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//编码器接口托管时钟,删除TIM_InternalClockConfig(TIM3);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//编码器接口托管计数器模式,配置无用TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//ARR满量程计数TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//编码器时钟驱动计数器,不分频TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);TIM_ICInitTypeDef TIM_ICInitStructure;//初始化结构体保证配置完整TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;TIM_ICInitStructure.TIM_ICFilter = 0x0F;/*输入捕获单元只需配置滤波器即可,极性使用定时器接口函数配置,避免重复配置TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;*/TIM_ICInit(TIM3, &TIM_ICInitStructure);//配置通道2TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;TIM_ICInit(TIM3, &TIM_ICInitStructure);//配置定时器接口TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_Cmd(TIM3, ENABLE);
}//使用16位有符号数实现负数表示
int16_t Encoder_Get(void)
{//读取单位时间内的计数变化量后清零int16_t Temp;Temp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3, 0);return Temp;
}

主程序

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"int16_t Speed;int main(void)
{OLED_Init();Encoder_Init();OLED_ShowString(1, 1, "Speed:");while(1){OLED_ShowSignedNum(1, 7, Encoder_Get(), 5);}
}//通过TIM2的定时中断实现每隔1s刷新速度
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update)){Speed = Encoder_Get();}
}

ADC数模转换器

ADC简介

  • ADC(Analog-Digital Converter)模拟-数字转换器
  • ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。与之相对的是使用加权电阻网络将数字量转换为模拟量的DAC数模转换器。
  • STM32的ADC为12位逐次逼近型ADC,转换时间为1μs,对应1MHz转换频率。12位是分辨率,位数越高,量化结果越精细。
  • 输入电压范围:0~3.3V,转换结果范围:0~4095,两者之间为一一对应的线性关系。
  • 18个输入通道,最多可测量16个外部(GPIO)和2个内部(内部温度传感器和参考电压)信号源。
  • 规则组和注入组两个转换单元。普通的AD转换流程是启动一次转换读一次值,STM32可一次性启动一个组以连续转换多个值,规则组用于常规使用,注入组用于突发事件。
  • 模拟看门狗自动监测输入电压范围,当AD值高于设定的上阈值或低于下阈值时会申请中断。
  • STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道。

逐次逼近型ADC

ADC0809是一个独立的8位逐次逼近型ADC芯片。IN0~IN7为8路输入通道,通过通道选择开关选择一路到比较器进行转换。地址锁存和译码接收3位通道号ADDA~ADDC,并在锁存信号ALE传入时拨好对应通路的开关。比较器的两个输入端一端为待测电压,另一端为已知编码的DAC输出。经比较后,如果DAC输出电压较小则增大DAC输入数据,如果DAC输出电压较大则减小DAC输入数据,直到DAC输出电压和外部通道输入电压近似相等,此时DAC输入数据即外部电压的编码数据。电压调节的过程由SAR完成,寻找未知电压的方法为二分法,对二进制来说就是从高位到低位依次判断是1还是0的过程,即“逐次逼近”。转换结束后,DAC逼近得到的输入数据通过缓冲器输出。除此之外,CLOCK为推动ADC逐次逼近的时钟,START为开始转换信号,EOC为转换结束信号。 V R E F ( + ) V_{REF(+)} VREF(+) V R E F ( − ) V_{REF(-)} VREF()为DAC参考电压,用于决定8位最大值所表示的电压,它也决定了ADC输入范围,因此也是ADC参考电压。通常参考电压的正极和 V C C V_{CC} VCC、负极和GND相同。

ADC框图

ADC左侧有ADCx_IN0~ADCx_IN15共16个GPIO输入通道和温度传感器与 V R E F I N T V_{REFINT} VREFINT两个内部通道。18个通道通过模拟多路开关指定想要选择的通道,并输出到模数转换器执行逐次逼近过程,转换结果存入通道数据寄存器以供读取。STM32可以一次性同时选中多个通道依次转换,注入组最多可选中4个,规则组最多可选中16个。但是规则组只有一个数据寄存器,因此需要配合DMA在每转换一个通道后将数据转移以便转换下一个通道,防止覆盖。

外围线路中,左下角为触发转换部分,相当于开始转换信号。STM32的ADC触发信号又两种,一种为手动调用代码软件触发,另一种为触发转换部分接收的硬件触发源,主要来自定时器和外部中断引脚,通常使用TRGO实现自动触发转换。左上角 V R E F + V_{REF+} VREF+ V R E F − V_{REF-} VREF为ADC参考电压, V D D A V_{DDA} VDDA V S S A V_{SSA} VSSA为ADC供电引脚,参考电压在内部与供电引脚接在一起。右侧ADDCLK为ADC用于驱动内部逐次逼近的时钟,来自于RCC时钟树的ADC预分频器,分频系数可以是2/4/6/8,但ADCCLK最大只支持14MHz,因此只能选六分频12MHz和八分频9MHz。DMA请求用于触发DMA进行数据转运。模拟看门狗可以存储一个阈值高限和阈值低限,如果启动了模拟看门狗并指定了看门通道,一旦看门通道电压超出阈值,看门狗就会申请模拟看门狗中断通往NVIC,而规则组和注入组在转换完成后也会产生转换结束信号将对应标志位置一,可选择触发中断。

ADC基本结构图

输入通道

其中STM32F103C8T6没有ADC3和PC0~PC5引脚。

转换模式

ADC初始化结构体包含两个参数分别选择单次/连续转换和扫描/非扫描模式,两者排列组合构成以下四种模式。

单次转换-非扫描模式

序列1~16写入需要转换的通道,在单次转换模式下只有序列1有效,一次只能选择一个通道。将通道写入序列1,触发转换,经过一个转换时间后转换完成,转换结果存储在数据寄存器,同时EOC标志位置一,转换过程结束。如果想再启动一次转换需要再触发一次,如果想更换通道需要在触发之前更改。

连续转换-非扫描模式

连续转换在一次转换之后不会停止,而是立刻开始下一轮的转换,一直持续下去。好处是无需继续手动开始转换,后续读取也无需判断是否转换结束。

单次转换-扫描模式

扫描模式可以指定通道数目参数,向序列写入多个通道,允许重复。每次触发之后,ADC依次对序列中前通道数目个位置进行转换,在所有通道转换完成之后产生EOC信号,转换结束。在扫描模式的情况下还有间断模式,其作用是在扫描的过程中每隔几个转换暂停一次,需要再次触发才能继续,仅供了解。

连续转换-扫描模式

触发控制

外部触发信号的选择通过配置EXTSEL寄存器来完成。

数据对齐

ADC转换结果为12位,但数据寄存器有16位,因此存在数据对齐的问题。一般使用右对齐,这样读取寄存器直接得到转换结果,而左对齐的结果还需除以 2 4 2^4 24。而当不需要12位精度时,使用左对齐可以方便地提取转换结果的高位,舍弃低位。

转换时间

AD转换步骤为采样-保持-量化-编码。其中量化编码为逐次逼近过程,位数越多耗时越长。采样保持将采样的电压存储进电容后断开采样开关再进行AD转换,以确保量化编码期间电压保持不变,精确定位未知电压位置,采样时间手动配置,时间越长越能避免毛刺信号干扰。由此STM32ADC总转换时间:

T C O N V T_{CONV} TCONV=采样时间+12.5个ADC周期

如果不需要极高的转换频率,可忽略转换时间。

校准

ADC有内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码,用于消除在随后的转换中每隔电容器上产生的误差。建议在每次上电后执行一次校准,启动校准前ADC必须处于关电状态超过两个ADC时钟周期。

硬件电路

上图为ADC实验外围电路设计。左侧通过电位器产生可调电压,滑动端往上滑电压增大,往下滑电压减小。中间将传感器电阻(等效为一个可变电阻)与一个固定电阻串联分压,传感器阻值变小,下拉作用变强,输出端(在传感器模块中为AO引脚)电压下降,反之升高。右侧为电压转换电路,将电压值超过ADC转换范围的电压通过分压转换为范围内的电压,但能力有限,过高的电压建议使用隔离放大器等专用采集芯片。

函数详解

RCC_ADCCLKConfig函数

简介:配置ADCCLK分频器。

参数:分频系数

RCC_PCLK2_Div2, RCC_PCLK2_Div4, RCC_PCLK2_Div6, RCC_PCLK2_Div8

ADC_DeInit函数

简介:恢复缺省配置。

参数:ADC名称

ADC1, ADC2, ADC3

ADC_Init函数

简介:配置ADC模块。

参数一:ADC名称

参数二:ADC_InitTypeDef结构体指针

ADC_InitTypeDef结构体

成员ADC_Mode:ADC工作模式

ADC_Mode_Independent(独立模式)
其余为双ADC模式,较为复杂,暂时不用

成员ADC_DataAlign:数据对齐

ADC_DataAlign_Right, ADC_DataAlign_Left

成员ADC_ExternalTrigConv:启动规则组转换的外部触发源

ADC_ExternalTrigConv_x(x对应ADC框图的触发源名称,其中CH改为CC)
ADC_ExternalTrigConv_None(不使用外部触发源)

成员ADC_ContinuousConvMode:是否启用连续转换

ENABLE, DISABLE

成员ADC_ScanConvMode:是否启用扫描模式

ENABLE, DISABLE

成员ADC_NbrOfChannel:扫描模式下的通道数目

1~16

ADC_StructInit函数

简介:初始化ADC_InitTypeDef结构体。

参数:ADC_InitTypeDef结构体指针

ADC_Cmd函数

简介:给ADC上电。

参数一:ADC名称

参数二:使能/失能

ADC_DMACmd函数

简介:开启DMA输出信号。

参数一:ADC名称

参数二:使能/失能

ADC_ITConfig函数

简介:中断输出控制。

参数一:ADC名称

参数二:ADC中断源

ADC_IT_EOC(转换结束), ADC_IT_AWD(模拟看门狗事件), ADC_IT_JEOC(注入转换结束)

参数三:使能/失能

校准函数

ADC_ResetCalibration函数(复位校准)

ADC_GetResetCalibrationStatus函数(获取复位校准状态)

ADC_StartCalibration函数(开始校准)

ADC_GetCalibrationStatus函数(获取开始校准状态)

参数:ADC名称。

在ADC初始化完成后依次调用即可。

ADC_SoftwareStartConvCmd函数

简介:启用软件触发转换。

参数一:ADC名称

参数二:使能/失能

ADC_GetSoftwareStartConvStatus函数(不常用)

简介:读取SWSTART位的转换开始信号状态。软件设置该位以启动转换,硬件在转换开始后马上清除此位。

参数:ADC名称

ADC_DiscModeChannelCountConfig函数

简介:配置间断模式每隔几个通道间断一次。

参数一:ADC名称

参数二:通道数

ADC_DiscModeCmd函数

简介:启用间断模式。

参数一:ADC名称

参数二:使能/失能

ADC_RegularChannelConfig函数

简介:配置ADC规则组通道。

参数一:ADC名称

参数二:指定通道

ADC_Channel_0, ..., ADC_Channel_17

参数三:序列位置(1~16)

参数四:采样时间(x取1/7/13/28/41/55/71/239,采样时间为x+0.5个ADC周期)

ADC_SampleTime_xCycles5

ADC_ExternalTrigConvCmd函数

简介:允许外部触发转换。

参数一:ADC名称

参数二:使能/失能

ADC_GetConversionValue函数

简介:读取ADC转换值。

参数:ADC名称

ADC_GetDualModeConversionValue函数

简介:双ADC模式读取转换值。暂时不用。

参数:void

中断标志位函数

ADC_GetFlagStatus函数

ADC_ClearFlag函数

参数一:ADC名称

参数二:ADC标志位

ADC_FLAG_AWD, ADC_FLAG_EOC, ADC_FLAG_JEOC
ADC_FLAG_JSTRT(注入组转换开始), ADC_FLAG_STRT(规则组转换开始)

ADC_GetITStatus函数

ADC_ClearITPendingBit函数

参数一:ADC名称

参数二:ADC中断源

实验16 AD单通道

接线图

ADC驱动

AD.h

#ifndef __AD_H
#define __AD_Hvoid AD_Init(void);
uint16_t AD_GetValue(void);#endif

AD.c

#include "stm32f10x.h"void AD_Init(void)
{//配置时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//ADC时钟为六分频12MHzRCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置PA0GPIO_InitTypeDef GPIO_InitStructure;//ADC使用模拟输入模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//选择规则组输入通道//将通道0写入序列1,采样时间要求不高随意ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//初始化ADC//单次转换-非扫描模式-右对齐-无外部触发源-独立模式ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_Init(ADC1, &ADC_InitStructure);//上电ADC_Cmd(ADC1, ENABLE);//校准ADC_ResetCalibration(ADC1);//校准完成后对应位清零跳出循环while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}//启动转换获取结果
uint16_t AD_GetValue(void)
{//软件触发转换ADC_SoftwareStartConvCmd(ADC1, ENABLE);//等待转换完成,转换结束标志位置一while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));return ADC_GetConversionValue(ADC1);//读取后硬件自动清除转换结束标志位
}

主程序

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "AD.h"uint16_t ADValue;
float Voltage;int main(void)
{OLED_Init();AD_Init();OLED_ShowString(1, 1, "ADValue:");OLED_ShowString(2, 1, "Voltage:0.00V");while(1){ADValue = AD_GetValue();//将AD值转换为电压值,为保留小数对ADValue做强制转换Voltage = (float)ADValue / 4095 * 3.3;OLED_ShowNum(1, 9, ADValue, 4);//尚未有OLED显示浮点数函数,将整数部分和小数部分分批显示OLED_ShowNum(2, 9, Voltage, 1);//取余需将浮点数转换为整数OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);//降低刷新频率Delay_ms(100);}
}

连续转换

连续转换需对AD.c做如下改动:

#include "stm32f10x.h"void AD_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);ADC_InitTypeDef ADC_InitStructure;//启用连续转换ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_Init(ADC1, &ADC_InitStructure);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));//软件触发只需在初始化执行一次ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}uint16_t AD_GetValue(void)
{//无需判断转换结束return ADC_GetConversionValue(ADC1);
}

实验17 AD多通道

接线图

ADC驱动

本实验通过单次转换-非扫描模式每次触发更改输入通道实现AD多通道,扫描模式与DMA将在下一节介绍。

AD.h

#ifndef __AD_H
#define __AD_Hvoid AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);#endif

AD.c

#include "stm32f10x.h"void AD_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//初始化PA0~PA3GPIO_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);ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_Init(ADC1, &ADC_InitStructure);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}//通过参数传入指定的通道
uint16_t AD_GetValue(uint8_t ADC_Channel)
{ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);ADC_SoftwareStartConvCmd(ADC1, ENABLE);while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));return ADC_GetConversionValue(ADC1);
}

主程序

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "AD.h"uint16_t AD0, AD1, AD2, AD3;int main(void)
{OLED_Init();AD_Init();OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:");while(1){AD0 = AD_GetValue(ADC_Channel_0);AD1 = AD_GetValue(ADC_Channel_1);AD2 = AD_GetValue(ADC_Channel_2);AD3 = AD_GetValue(ADC_Channel_3);OLED_ShowNum(1, 5, AD0, 4);OLED_ShowNum(2, 5, AD1, 4);OLED_ShowNum(3, 5, AD2, 4);OLED_ShowNum(4, 5, AD3, 4);Delay_ms(100);}
}

相关文章:

【STM32单片机】#8 定时器编码器接口ADC模数转换器

主要参考学习资料: B站江协科技 STM32入门教程-2023版 细致讲解 中文字幕 开发资料下载链接:https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwddspb 单片机套装:STM32F103C8T6开发板单片机C6T6核心板 实验板最小系统板套件科协 实验&…...

dify部署,ollama部署,拉取模型,创建ai聊天应用

dify下载安装 dify1.0.1 windos安装包百度云盘地址 通过网盘分享的文件:dify-1.0.1.zip 链接: 百度网盘 请输入提取码 提取码: 1234 dify安装包 linux安装包百度云盘地址 通过网盘分享的文件:dify-1.0.1.tar.gz 链接: 百度网盘 请输入提取码 提取码…...

213、【图论】有向图的完全联通(Python)

题目描述 原题链接:105. 有向图的完全联通 代码实现 import collectionsn, k list(map(int, input().split())) adjacency collections.defaultdict(list) for _ in range(k):head, tail list(map(int, input().split()))adjacency[head].append(tail)visited_…...

Node.js中util模块详解

Node.js 中 util 模块全部 API 详解 一、类型检查函数 const util require(util);// 1. util.types // 检查对象类型 console.log(util.types.isDate(new Date())); // true console.log(util.types.isRegExp(/abc/)); // true console.log(util.types.isArrayBuffer(new …...

BasicTS:全面基准测试与异质性分析

BasicTS:全面基准测试与异质性分析 在当今数字化时代,多元时间序列(Multivariate Time Series, MTS)分析在众多领域发挥着关键作用,从交通管理到能源系统优化,都离不开对MTS的精准预测。然而,当…...

认识python全栈框架reflex:快速打造工具类网站、模型调用web应用

以下是对reflex的简单介绍: 纯Python编写的,高性能、可自定义的 Web 应用开发框架 网页开发内置组件生态完整,灵活使用、快速接入、快速部署支持路由页面,可以开发复杂系统、企业级系统,这方面优于gradio、streamlit…...

课题申报的立项依据方位指南:使用DeepSeek提高课题立项的关键

在竞争日益激烈的学术研究和科研项目申报环境中,立项依据作为课题申报书的灵魂部分,往往决定着一项研究能否获得评审专家的青睐和资助。 然而,许多研究者尽管学术能力突出,却在立项依据的撰写上显得力不从心,导致优质…...

蓝桥杯电子赛_E2PROM(AT24C02)

目录 一 前言 二 E2PROM的相关讲解 AT24C02的地址 PCF8591的地址 三 根据提供的iic写代码 相关可能会有疑问的地方: 1 三个入口参数,都有什么用? 2 为什么在写中,要用IIC_SendByte,在读中,要用IIC_R…...

Kubernetes服务注册到consul流程实践

文章目录 前言架构图示意一、环境准备二、consul部署1.yaml示例2.consul部署验证 三、consulctl工具实现1.核心功能2.注册到consul的标签及元数据3.consulctl工具使用示例 四、通过Dockerfile构建consulctl工具镜像五、Kubernetes集成方案六、 结果验证1.注册验证2.销毁验证 总…...

供应链业务-供应链全局观(三)- 供应链三流的集成

概述 供应链的全局观的全两篇文章主要描述了供应链的基础概念和供应链的协作和集成问题。 供应链业务-供应链全局观(一)定义了什么是供应链和供应链管理。 所谓供应链就是把采购进来的东西,通过自身的生成加工,进行增值服务&am…...

Docker 提示Docker Engine stopped

做AI开发的时候,安装Docker提示Docker Engine stopped,以下是解决步骤: 一般都是成功的,不成功很可能是电脑兼容问题,通过采用4.4.4版本解决的: docker desktop 4.4.4 旧版本下载:在这里找到了4…...

对自己的优缺点评价

在面试中回答优缺点时,需要既体现自我认知的客观性,又能将优缺点与岗位需求结合,避免暴露可能影响工作的硬伤。以下是一个符合Java开发者角色的回答框架,供参考: 回答思路: 优点:选择与岗位直接…...

解决eNSP在24H2版本下AR_40启动失败问题

前言 1.网络学习中缺少不了模拟,自从Windows版本更新24H2以后,eNSP就出现各种问题,最常见的就是AR报错40【启动失败】,之前我也去网站搜了,也问了Microsoft社区,发现他们在底层逻辑上进行了修改(开启了虚拟…...

计算机组成原理-指令系统

1. 指令系统的定义与作用 指令系统(Instruction Set Architecture, ISA)是计算机硬件与软件之间的接口规范,定义了CPU能够识别和执行的所有指令的集合,是计算机体系结构的核心组成部分。 核心作用: 为程序员提供操作…...

Oracle数据库中 LEVEL start with prior connect by

在Oracle数据库中,处理层次结构数据是一项常见且重要的任务。无论是组织结构、分类目录还是其他具有层级关系的数据,Oracle都提供了强大的工具来简化和优化这些操作。其中,LEVEL伪列结合CONNECT BY和START WITH关键字,成为了处理层…...

HTTP 1.1 比 HTTP1.0 多了什么?(详尽版)

相较于HTTP 1.0,1.1 版本增加了以上特性: 1. 新增了连接管理即 keepalive,允许持久连接。 定义: Keepalive允许客户端和服务器在完成一次请求-响应后,保持连接处于打开状态,以便后续请求复用同一连接&am…...

Java学习手册:Java I/O与NIO

Java I/O(Input/Output)和NIO(New Input/Output)是Java语言中用于处理输入输出操作的重要部分。它们提供了丰富的API来处理文件和网络通信。I/O是Java早期版本中引入的,而NIO是在Java 1.4中引入的,旨在提供…...

linux下的目录文件管理和基本文件管理的基本操作

目录 1.目录创建,文件创建和文件编辑的案例 2.文件编辑进阶 --vim 3. 命令的别名 4. 查看文件内容和文件编辑(重定向)的案例 5. 重定向之追加 6. 查看目录和文件编辑的案例 7. 查看目录和文件编辑(覆盖)的案例 为了加深对linux命令的熟悉程度,这…...

magnet库Hello,world!

1.c文件 #include<iostream> #include"Control.hpp" class O1:public mag::Control{bool b; public:O1(){b1;}bool decide(){return b&&islifing();}void action(){std::cout<<"Hello,world!\n";b0;destroy();} }; int main(){O1 o1;…...

应急响应靶机-Linux(1)

挑战内容 账户密码&#xff1a;defend/defend Root/defend 黑客的IP地址遗留下的三个flag 1、按正常思路来走&#xff0c;先登录一手因已经给出root账户密码&#xff0c;先查看一手执行过的命令&#xff0c;发现一个flag值并且看到他往期编辑了一个文件&#xff0c;咱们顺便进去…...

k8s 部署spring项目+动态启动pod

在 Kubernetes 中部署 Spring Boot 项目并实现 动态管理 Pod&#xff08;自动扩缩容、滚动更新等&#xff09;&#xff0c;需要结合 Docker 镜像构建、Deployment 配置、Service 暴露和 HPA&#xff08;Horizontal Pod Autoscaler&#xff09; 等组件。以下是完整操作步骤&…...

【DINO】

detr 简化了检测流水线,消除了许多手工设置的组件 单阶段目标检测 需要前置的backbone抽取特征 faster_rcnn和yolo都是基于anchor,anchor当作候选框,NMS非极大值抑制 重叠的框只保存一个,效率低 所以detr来了,transformer,既有encoder又有decoder,套一个transforme…...

Nature重磅:后晶体管时代光子芯片革新AI计算!光子处理器运行《吃豆人》性能比肩电子,能效提升超500倍

随着人工智能&#xff08;AI&#xff09;模型规模以及应用范围的不断拓展&#xff0c;性能上限和能耗瓶颈正逐渐显现出来。大语言模型&#xff08;LLM&#xff09;、强化学习和卷积神经网络等 AI 模型的复杂性不断增长&#xff0c;正在将传统电子计算推向极限&#xff0c;能源需…...

Excel表格文件分组归并——通过sql

将 Excel 表转换为 SQL 数据库并直接执行 SQL 查询以获得所需的输出。以下是使用 SQL 实现此目的的步骤&#xff1a; 第 1 步&#xff1a;将 Excel 数据导入 MySQL 假设您设置了 MySQL 数据库&#xff0c;则需要先将 Excel 数据导入到表中。您可以使用语句或工具&#xff08;…...

2.微服务拆分流程

文章目录 交易服务1.1.创建项目1.2.引入依赖1.3.创建交易服务启动类1.4.创建并编写配置文件1.5.代码连接池4.2.1.引入依赖4.2.2.开启连接池抽取Feign客户端 1.6.抽取ItemClient接口1.7.抽取CartClient接口改造OrderServiceImpl扫描包 1.8.数据库1.9.配置启动项1.10.测试 以拆分…...

vue入门:计算属性computer监听器watch

文章目录 计算属性computer定义计算属性在模板中使用计算属性计算属性的使用场景 监听器watch基本语法深度监听立即执行监听数组异步操作数据校验副作用处理清理监听器 watch 与 computed 的区别 计算属性computer 在 Vue 中&#xff0c;计算属性&#xff08;computed&#xf…...

Jenkins 发送钉钉消息

这里不介绍 Jenkins 的安装&#xff0c;可以网上找到很多安装教程&#xff0c;重点介绍如何集成钉钉消息。 需要提前准备钉钉机器人的 webhook 地址。&#xff08;网上找下&#xff0c;很多教程&#xff09; 下面开始配置钉钉机器人&#xff0c;登录 Jenkins&#xff0c;下载 …...

numpy练习

生成一个2行3列随机整数二维数组a使用Numpy方法对&#xff08;1&#xff09;中数组a进行整体求积使用Numpy方法对&#xff08;1&#xff09;中数组a进行求每列最大值索引定义一个NumPy一维数组 b&#xff0c;元素为 1 到 10 的整数获取&#xff08;4&#xff09;数组b中最后五个…...

Ethers.js 开发入门:核心功能、最佳实践与避坑指南

引言 Ethers.js 是当前 Web3 开发领域增长最快、备受开发者青睐的以太坊 JavaScript 库之一。在本篇文章中&#xff0c;我们将介绍 Ethers.js 的核心功能和用法&#xff0c;包括如何连接区块链节点、与钱包交互、读取智能合约数据、发送交易等。同时&#xff0c;我们还将分享使…...

SQL查询语句的​​书写顺序​​

一、标准SQL书写顺序&#xff08;逻辑顺序&#xff09; 书写顺序是开发者编写SQL时遵循的语法规则&#xff0c;逻辑上更贴近“声明式”需求描述。以下是​​从前往后​​的书写顺序&#xff1a; SELECT[DISTINCT] 列名或表达式 FROM表名或子查询 [JOIN ... ON ...] WHERE行级…...

探索加密期权波动率交易的系统化实践——动态对冲工具使用

Trading Volatility – What Are My Options? 在本文中&#xff0c;我们将介绍一些如何交易资产波动性&#xff08;而非资产价格&#xff09;的示例。为了帮助理解&#xff0c;我们将使用 Deribit 上提供的几种不同产品&#xff0c;包括但不限于期权。我们将尽可能消除对标的价…...

文件操作和 IO - 3

目录 文件内容的读写 —— 数据流 InputStream 概述 方法&#xff1a; 说明&#xff1a; FileInputStream 概述 read 方法&#xff1a; OutputStream 概述 方法 说明 FileOutputStream 概述 write 方法&#xff1a; Reader 字符流 Writer 字符流 总结&#xff1a…...

Kubernetes中的Label和Selector核心作用与应用场景

一. Label 和 Selector 的核心概念 Label 和 Selector 是 Kubernetes 中实现灵活资源管理的基石&#xff0c;贯穿部署、服务发现、监控等核心场景。通过合理设计标签&#xff0c;用户可以高效实现自动化运维与精准资源控制。 Label&#xff08;标签&#xff09;&#xff1a; K…...

L1-6 大勾股定理

题目 大勾股定理是勾股定理的推广&#xff1a;对任何正整数 n 存在 2n1 个连续正整数&#xff0c;满足前 n1 个数的平方和等于后 n 个数的平方和。例如对于 n1 有 3^2 4^2 5^2 &#xff1b;n2 有 10^2 11^2 12^2 13^2 14^2 等。给定 n&#xff0c;本题就请你找出对应的解。 输…...

esp32-idf Linux 环境安装教程

一、提前说明 1. 系统环境 Ubuntu22.04 2. 适配芯片 ESP32S3 3. idf版本 v5.4.1(截止2025年4月13日为最新版本) 二、安装步骤 1. 安装前置依赖 sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev l…...

关于使用 nuitka进行构建python应用的一些配置,以及github action自动构建;

1. 通用配置 # 设置输出目录和文件名output_dir "dist"app_name "CursorAutoFree"# 基础命令行选项base_options ["--follow-imports", # 跟踪导入"--enable-plugintk-inter", # 启用 Tkinter 支持"--include-packagecusto…...

C++开山解惑

. Solution & Code 本题解仅适用于 C 选手。 这道题可谓是 C 中最基础的题目之一&#xff0c;先上两份代码&#xff1a; #include <cstdio> using namespace std;int main() {long long a, b;scanf("%lld%lld", &a, &b);printf("%lld"…...

Pytorch深度学习框架60天进阶学习计划 - 第41天:生成对抗网络进阶(二)

Pytorch深度学习框架60天进阶学习计划 - 第41天&#xff1a;生成对抗网络进阶&#xff08;二&#xff09; 7. 实现条件WGAN-GP # 训练条件WGAN-GP def train_conditional_wgan_gp():# 用于记录损失d_losses []g_losses []# 用于记录生成样本的多样性&#xff08;通过类别分…...

路由策略/策略路由之route-policy

思科名称&#xff1a;route-map、match、set Route-policy 是一个非常重要的基础性策略工具。你可以把它想象成一个拥有多个节点&#xff08;node&#xff09;的列表&#xff08;这些 node 按编号大小进行排序&#xff09;。在每个节点中&#xff0c;可以定义条件语句及执行语…...

《嵌入式系统原理》一些题目

1 &#xff0e;ARM 的存储格式&#xff1f;默认的存储模式是&#xff1f; 大端格式和小端格式&#xff0c;默认为小端模式 2 &#xff0e;当前程序状态寄存器&#xff1f;&#xff08;英文简写、条件码标志位及控制位的含义&#xff09; CPSR&#xff0c;N,Z,C,V(P26) 3 &a…...

卡洛诗已悄然改写高性价比西餐的竞争规则

在餐饮行业竞争日益激烈的今天&#xff0c;消费者对“高性价比”的定义已从单纯的低价转向品质、体验与情感价值的综合考量。萨莉亚原团队成员出来升级孵化的新概念中式西餐卡洛诗以“访九州异馔&#xff0c;再造东方味”为核心理念&#xff0c;通过本土化创新、严控文化及场景…...

独立开发者之网站的robots.txt文件如何生成和添加

robots.txt是一个存放在网站根目录下的文本文件&#xff0c;用于告诉搜索引擎爬虫哪些页面可以抓取&#xff0c;哪些页面不可以抓取。下面我将详细介绍如何生成和添加robots.txt文件。 什么是robots.txt文件&#xff1f; robots.txt是遵循"机器人排除协议"(Robots…...

02核心-EffectSpec,EffectContext

1.FGameplayEffectSpec 效果Spec 创建&#xff1a;MakeOutGoingSpec>EffectSpecHandle≈EffectSpec. 创建总结&#xff1a;EffectLevelEffectContext>EffectSpec(Handle) 数据&#xff1a;EffectSpec存有效果的等级&#xff0c;上下文&#xff0c;类。 还有很多其他东…...

驱动开发硬核特训 · Day 10(下篇):设备模型实战篇 —— Platform 驱动机制 ≈ 运行时适配器

&#x1f50d; B站相应的视屏教程&#xff1a; &#x1f4cc; 内核&#xff1a;博文视频 - 总线驱动模型实战全解析 敬请关注&#xff0c;记得标为原始粉丝。 &#x1f527; &#x1f4cd; 一、目标与回顾 在上篇《理论篇》中&#xff0c;我们从软件工程角度&#xff0c;解释…...

集合框架二三事

一.集合框架 Java集合框架&#xff08;Java Collections Framework&#xff09;是Java标准库中用于存储和处理对象集合的一组接口和实现类。它提供了一套统一的API&#xff0c;使得开发者能够高效地管理和操作数据集合。以下是关于Java集合框架的详细介绍&#xff0c;包括其核…...

前端jest(vitest)单元测试快速手上

前言 vitest和jest除了配置上不同&#xff0c;其他的基本差不多&#xff0c;这里以jest为例进行说明 安装依赖 npm install -D jest编写测试 例如&#xff0c;我们将编写一个简单的测试来验证将两个数字相加的函数的输出。 sum.js export function sum(a, b) {return a b…...

优化方法介绍(二)

优化方法介绍(二) 本博客是一个系列博客,主要是介绍各种优化方法,使用 matlab 实现,包括方法介绍,公式推导和优化过程可视化 1 BFGS 方法介绍 BFGS 的其实就是一种改良后的牛顿法,因为计算二阶导数 Hessian 矩阵所需的计算资源是比较大的,复杂度为 O ( 2 ⋅ n 2 ) …...

Sklearn入门之datasets的基本用法

、 Sklearn全称:Scipy-toolkit Learn是 一个基于scipy实现的的开源机器学习库。它提供了大量的算法和工具&#xff0c;用于数据挖掘和数据分析&#xff0c;包括分类、回归、聚类等多种任务。本文我将带你了解并入门Sklearn下的datasets在机器学习中的基本用法。 获取方式 pi…...

UDS协议 - 应用层服务测试用例概览

目录 前言一、10服务物理寻址测试功能寻址测试二、11服务物理寻址测试功能寻址测试三、14服务物理寻址测试功能寻址测试四、19服务物理寻址测试功能寻址测试五、22服务物理寻址测试功能寻址测试六、27服务物理寻址测试七、28服务物理寻址测试功能寻址测试八、2E服务物理寻址测试…...

记录一个虚拟机分配资源的问题

Virtualize Intel VT - x/EPT or AMD - V/RVI&#xff1a;若物理机的 CPU 支持对应的硬件虚拟化技术&#xff08;Intel VT - x 或 AMD - V&#xff09;&#xff0c;强烈建议开启。该功能可显著提升虚拟机的性能&#xff0c;让虚拟机更高效地利用物理 CPU 资源&#xff0c;改善卡…...