🚀write in front🚀
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝💬本系列哔哩哔哩江科大STM32的视频为主以及自己的总结梳理📚
🚀Projeet source code🚀
💾工程代码放在了本人的Gitee仓库:iPickCan (iPickCan) - Gitee.com
引用:
STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili
Keil5 MDK版 下载与安装教程(STM32单片机编程软件)_mdk528-CSDN博客
STM32之Keil5 MDK的安装与下载_keil5下载程序到单片机stm32-CSDN博客
0. 江协科技/江科大-STM32入门教程-各章节详细笔记-查阅传送门-STM32标准库开发_江协科技stm32笔记-CSDN博客
【STM32】江科大STM32学习笔记汇总(已完结)_stm32江科大笔记-CSDN博客
江科大STM32学习笔记(上)_stm32博客-CSDN博客
STM32学习笔记一(基于标准库学习)_电平输出推免-CSDN博客
STM32 MCU学习资源-CSDN博客
stm32学习笔记-作者: Vera工程师养成记
stem32江科大自学笔记-CSDN博客
术语:
英文缩写 | 描述 |
GPIO:General Purpose Input Onuput | 通用输入输出 |
AFIO:Alternate Function Input Output | 复用输入输出 |
AO:Analog Output | 模拟输出 |
DO:Digital Output | 数字输出 |
内部时钟源 CK_INT:Clock Internal | 内部时钟源 |
外部时钟源 ETR:External clock | 时钟源 External clock |
外部时钟源 ETR:External clock mode 1 | 外部时钟源 Extern Input pin 时钟模式1 |
外部时钟源 ETR:External clock mode 2 | 外部时钟源 Extern Trigger 时钟模式2 |
外部时钟源 ITRx:Internal trigger inputs | 外部时钟源,ITRx (Internal trigger inputs)内部触发输入 |
外部时钟源 TIx:external input pin | 外部时钟源 TIx (external input pin)外部输入引脚 |
CCR:Capture/Comapre Register | 捕获/比较寄存器 |
OC:Output Compare | 输出比较 |
IC:Input Capture | 输入捕获 |
TI1FP1:TI1 Filter Polarity 1 | Extern Input 1 Filter Polarity 1,外部输入1滤波极性1 |
TI1FP2:TI1 Filter Polarity 2 | Extern Input 1 Filter Polarity 2,外部输入1滤波极性2 |
正文:
0. 概述
从 2024/06/12 定下计划开始学习下江协科技STM32课程,接下来将会按照哔站上江协科技STM32的教学视频来学习入门STM32 开发,本文是视频教程 P2 STM32简介一讲的笔记。
定时器共四个部分,分为八个小节笔记。本小节为第一部分第一节。
🌳在第一部分,是定时器的基本定时的功能:定时中断功能、内外时钟源选择
🌳在第二部分,是定时器的输出比较功能,最常见的用途是产生PWM波形,用于驱动电机等设备
🌳在第三部分,是定时器的输入捕获功能和主从触发模式,来实现测量方波频率
🌳在第四部分,是定时器的编码器接口,能够更加方便读取正交编码器的输出波形,编码电机测速
1.🚢第一个代码:AD单通道
接线图:
电位器的内部结构是这样的:
左边和右边的两个引脚接的是电阻的两个固定端,中间这个引脚接的是滑动抽头。电位器外边这里有个十字形状的槽,可以拧,往左拧抽头就往左靠,往右拧,抽头就往右靠。所以外围电路这里,我们把左边的固定端接在负极,右边的固定端接在正极,中间就可以输出,从负极到正极可调的电压了,把可调的电压输出接在PA0。
2.🚢ADC初始化步骤
AD的初始化看这个结构图
ADC初始化的步骤具体的步骤:
🦄第一步,开启RCC时钟,包括ADC和GPIO的时钟。另外这里ADC CLK的分频器也需要配置一下。
🦄第二步,配置GPIO,把需要用的GPIO配置成模拟输入的模式。
🦄第三步,配置多路开关
把左边的通道接入到右边的规则组列表里。这个过程就是我们之前说的点菜,把各个通道的菜列在菜单里。
🦄第四步,配置ADC转换器,在库函数里是用结构体来配置的,可以配置这一大块电路的参数。
如果需要模拟看门狗,会有几个函数用来配置阈值和监测通道的。
如果想开启中断,就在中断输出控制里用ITconfig函数开启对应的中断输出,然后再在NVIC里配置一下优先级,这样就能触发中断了。
不过模拟看门狗中断我们本节暂时不用。
第五步,开关控制,调用一下ADC_Cmd的函数开启ADC。
这样ADC就配置完成了就能正常工作了。
第六步,校准
当然在开启ADC之后,根据手册里的建议,我们还可以对ADC进行一下校准,这样可以减小误差。在ADC工作的时候,如果想要软件触发转换,会有函数可以触发。如果想读取转换结果,也会有函数,可以读取结果。这个等会介绍扩函数的时候就可以看到了。
3.🚢ADC相关的库函数
首先我们看一下ADC CLK的配置函数,打开这个rcc.h文件,拖到最后。
这个函数是用来配置ADC CLK分频器的。它可以对APB2的72MHz时钟选择二、四、六、八分频,输入到ADC CLK,这就是这个函数的作用。
然后我们找一下ADC的库函数,打开adc.h文件,拖到最后。
三个初始化相关函数
这三个函数和其它模块的库函数一样,都是老朋友,不用多讲了。
ADC_Cmd
这个是用于给ADC上电的,也就是这里的开关控制
ADC_DMACmd
这个是用于开启DMA输出信号的。如果使用DMA转运数据,就得调用这个函数。这个我们下节讲DMA的时候再用。
ADC_ITConfig
中断输出控制,也就是这里用于控制某个中断能不能通过NVIC
四个校准相关函数
接下来这里有四个函数
分别是复位校准、获取复位校准状态、开始校准、获取开始校准状态,这就是用于控制校准的函数。我们在ADC初始化完成之后依次调用就行了。
ADC_SoftwareStartConvCmd
ADC软件开始转换控制,这个就是用于软件触发的函数了,调用一下就能软件触发转换了,也就是这里的触发控制,我们目前使用软件触发。
ADC_GetSoftwareStartConvStatus
ADC获取软件开始转换状态,从名字上来看,这个函数好像是判断转换是不是正在进行的。我们是不是可以调用这个函数来判断转换是否已经结束?答案是不行的。这个函数就是用来获取CR2的SWSTART这一位。
在手册里可以看到这一位的作用是开始转换规则通道,由软件设置该位以启动转换,转换开始后硬件马上清除此位。
因此,ADC_SoftwareStartConvCmd这个函数就是给SWSTART位置1,以开始转换的。
而ADC_GetSoftwareStartConvStatus这个函数是返回SWSTART的状态。
由于SWSTART位在转换开始后立刻清零了。所以这个函数的返回值跟转换是否结束毫无关系。
那如何才能知道转换是否结束?
我们需要用到下面这个函数:
ADC_GetFlagStatus
获取标志位状态,然后参数给EOC的标志位,判断EOC标志位是不是置1了。如果转换结束,EOC标志位置1,然后调用这函数判断标志位。这样才是正确的判断转换是否结束的方法。
所以ADC_GetSoftwareStartConvStatus这个函数其实没啥用,我们一般不用,不要被它误导了。
然后下面这两个函数是用来配置间断模式的。
第一个函数是每隔几个通道间断一次。第二个函数是是不是启用间断模式。需要间断模式的话,可以了解一下。
ADC_RegularChannelConfig
DC规则组通道配置,这个函数比较重要。它的作用就是给序列的每个位置填写指定的通道,就是填写点菜菜单的过程
第一个参数是ADCx,第二个ADC channel就是理想指定的通道。第三个rank就是序列几的位置。然后第四个sample time就是指定通道的采样时间。
ADC_ExternalTrigConvCmd
ADC外部触发转换控制,就是是否允许外部触发转换。
ADC_GetConversionValue
ADC获取转换值,这个函数也比较重要,就是获取AD转换的数据寄存器,读取转换结果,就要使用这个函数。
ADC_GetDualModeConversionValue
之后,ADC获取双模式转换值,这个是双ADC模式读取转换结果的函数,我们暂时不用。
以上这些函数就是对ADC的一些基本功能和规则组的配置。
九个配置ADC注入组的函数
然后接下来这里有一大批函数,里面都带了一个injected,就是注入组的意思。
这一大批函数都是对ADC注入组进行配置的。
三个模拟看门狗配置的函数
然后下面的这三个函数就是对模拟看门狗进行配置的。
第一个是是否启动模拟看门狗。第二个是配置高低阈值。第三个是配置看门的通道。
ADC_TempSensorVrefintCmd
ADC温度传感器内部参考电压控制,这个是用来开启内部的两个通道的。如果你要用这两个通道,得调用一下这个函数开启一下,要不然是读不到正确的结果的。
四个获取或清除标志位函数
分别是获取标志位状态、清除标志位、获取中断状态、清除中断挂起位,这些函数也是常用函数了,不用多说。
看完这些函数我们来开始写代码。
4.🚢代码实现
//配置ADC是工作在独立模式,还是双ADC模式
//独立模式,ADC1和ADC2各转换各的
#define ADC_Mode_Independent ((uint32_t)0x00000000)
//其余的都是双ADC模式,相对比较复杂
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000)
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000)
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000)
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000)
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000)
#define ADC_Mode_RegSimult ((uint32_t)0x00060000)
#define ADC_Mode_FastInterl ((uint32_t)0x00070000)
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000)
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000)
ADC校准,如下四个函数对应ADC校准的四个步骤
//1.复位校准
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
//2.等地复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
//3.开始校准
void ADC_StartCalibration(ADC_TypeDef* ADCx);
//4.等待校准完成
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
4.1 实验1-单ADC转换
代码 AD.C
#include "stm32f10x.h" // Device header
#include "AD.h"
void AD_Init(void)
{
//开启RCC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //APB2 GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //APB2 GPIOA时钟
//RCC_ADC Clock预分频值
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //RCC_ADC_Clock=72Mhz/6=12Mhz
//GPIOA_Pin0配置为模拟输入
//GPIOA_Pin0作为ADC1的输入通道1
GPIO_InitTypeDef gpioInitStructure;
gpioInitStructure.GPIO_Mode = GPIO_Mode_AIN;
gpioInitStructure.GPIO_Pin = GPIO_Pin_0;
gpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioInitStructure);
//ADC模拟输入多路开关选择
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
//ADC_Init初始化
ADC_InitTypeDef ADC_InitStruct;
ADC_StructInit(&ADC_InitStruct);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //非外部触发,软件触发方式
ADC_InitStruct.ADC_ScanConvMode = DISABLE; //ADC使用非扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; //ADC使用非连续转换模式(单次模式)
ADC_InitStruct.ADC_NbrOfChannel = 1; //ADC规则组转换列表里的数目
ADC_Init(ADC1, &ADC_InitStruct);
//ADC开关
ADC_Cmd(ADC1, ENABLE);
//ADC校准
ADC_ResetCalibration(ADC1); //软件置标志位
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //当校准完成之后,硬件自动清除标志位
ADC_StartCalibration(ADC1); //软件置标志位
while(ADC_GetCalibrationStatus(ADC1) == SET); //当校准完成之后,硬件自动清除标志位
}
uint16_t GetValue(void)
{
uint16_t ADCValue;
//软件ADC触发转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
//等待ADC完成
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
ADCValue = ADC_GetConversionValue(ADC1); //读取清除EOC标志位
return ADCValue;
}
代码AD.h
#ifndef __AD_H__
#define __AD_H__
void AD_Init(void);
uint16_t GetValue(void);
#endif
代码 main.c
#include "stm32f10x.h" // Device header
#include "oled.h"
#include "Countersensor.h"
#include "Encoder.h"
#include "Timer.h"
#include "AD.h"
#include "Delay.h"
extern uint16_t Num;
int main(int argc, char *argv[])
{ uint16_t ADValue;
OLED_Init();
OLED_ShowString(1, 1, "Cnt:");
AD_Init();
OLED_ShowString(1, 1, "ADValue:");
while(1)
{
ADValue = GetValue();
OLED_ShowNum(2, 1, ADValue, 6);
Delay_ms(100);
}
return 1;
}
实验结果: