STM32:学习笔记

B站STM32入门教程:

https://www.bilibili.com/video/BV1th411z7sn/?spm_id_from=333.337.search-card.all.click

目录

一、GPIO:

(1)输出/输入模式

(2)GPIO配置

二、中断

(1)外部中断配置:

三、定时器

(1)结构框图

(2)定时器定时中断:

         (2.1)内部时钟驱动

        (2.2)外部时钟驱动

(3)输出比较模式 OC

(4)输入捕获模式 IC

        (4.1)输入捕获模式

        (4.2)PWMI模式

(5)编码器接口模式

四、ADC

(1)结构框图

(2)单通道AD转换

(3)多通道AD转换

五、DMA

(1)软件触发DAM

(2)硬件触发DAM

待更。。。。

一、GPIO:

        General Purpose Input/Output 是一种通用输入输出接口

(1)输出/输入模式

上拉输入: 当输入引脚悬空时,电压保持在高电平(Vcc)
下拉输入: 当输入引脚悬空时,电压保持在第电平(Gnd)
浮空输入: 引脚为高阻态,无上下拉,但容易受到噪声干扰
模拟输入: 当GPIO口用于向内部ADC输入模拟电压时使用
推挽输出: 可输出高低电平,均有驱动能力。可以驱动负载(如LED、蜂鸣器等),并可以输出高电平或低电平信号。
复用推挽输出: 允许将GPIO引脚用作特定外设功能,并具有推挽输出的特性。(如PWM、串口通信等复用功能)
开漏输出: 输出低电平、高阻态。在开漏配置下,引脚可以被外部上拉电阻拉至高电平,当引脚输出低电平时,电路闭合;当引脚输出高阻抗时,电路则处于断开状态  
复用开漏输出: 允许将GPIO引脚用作特定外设功能,并具有开漏输出的特性。(如I2C总线通信引脚等等)

(2)GPIO配置

void LED_init()//初始化GPIOA及管脚0,默认初始状态为1
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);//使能GPIOA的时钟

	GPIO_InitTypeDef GPIO_structure;             //定义GPIO类型的结构体
	GPIO_structure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度:50MHz
	GPIO_structure.GPIO_Mode  = GPIO_Mode_Out_PP;//输出模式:推挽输出
	GPIO_structure.GPIO_Pin   = GPIO_Pin_0;      //引脚选择:Pin_0
    GPIO_Init(GPIOA , &GPIO_structure);          //GPIO初始化函数	
	GPIO_SetBits(GPIOA, GPIO_Pin_0);             //默认初始状态为1
}

二、中断

(1)外部中断配置:

        外部中断功能:某些GPIO引脚可以配置为外部中断。当引脚状态变化(如从低变高或从高变低)时,可以触发中断执行处理程序。

以配置GPIOB的15引脚为外部中断源为例
步骤1:初始化GPIO
步骤2:设置EXTI线路
步骤3:设置NVIC开启中断
*********************************************************************
void CountSensor_init()//对射式红外传感器初始化(计数传感器)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);//开启GPIOB的时钟
	GPIO_InitTypeDef GPIO_structure;             //定义GPIO类型的结构体
	GPIO_structure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度:50MHz
	GPIO_structure.GPIO_Mode  = GPIO_Mode_IPD;   //输出模式:下拉输入(防止传感器杜邦线送了导致悬空,上拉也可以)
	GPIO_structure.GPIO_Pin   = GPIO_Pin_15;     //引脚选择:Pin_15
    GPIO_Init(GPIOB , &GPIO_structure);          //GPIO初始化函数	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO  , ENABLE);//开启AFIO的时钟
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource15);//将GPIOB的15脚作为外部终端源
	EXTI_InitTypeDef EXTI_structure;					//定义EXTI类型的结构体
	EXTI_structure.EXTI_Line    = EXTI_Line15;          //EXTI_线路15(对应中断源引脚号)
	EXTI_structure.EXTI_LineCmd = ENABLE;				//使能EXTI_线路
	EXTI_structure.EXTI_Mode    = EXTI_Mode_Interrupt;	//中断模式
	EXTI_structure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿触发
	EXTI_Init(&EXTI_structure);							//EXTI初始化函数		

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级
	NVIC_InitTypeDef NVIC_structure;                                   //定义NVIC类型的结构体
	NVIC_structure.NVIC_IRQChannel                   = EXTI15_10_IRQn; //中断通道(不同芯片通道有区别,跳转定义查看) 
	NVIC_structure.NVIC_IRQChannelPreemptionPriority = 0;			   //设置抢占优先级为0
	NVIC_structure.NVIC_IRQChannelSubPriority		 = 0;			   //设置响应优先级为0
	NVIC_structure.NVIC_IRQChannelCmd				 = ENABLE;         //使能中断通道
	NVIC_Init(&NVIC_structure);//EXTI初始化函数
}
//中断函数(不需要在头文件声明,函数名取startup_stm32f10x_md.s启动文件内对应中断函数名称)
//中断函数可写在mian函数或其他需要使用的位置:如中断内要操作mian函数内变量或使用其头文件内函数,就写在mian函数内
void EXTI15_10_IRQHandler()//中断函数模板
{
	if (EXTI_GetITStatus(EXTI_Line15) == SET)
	{
		//这里写中断函数
		EXTI_ClearITPendingBit(EXTI_Line15);//必须清除中断待处理位,表示已经完成中断处理,否则一直卡在中断
	}
}

三、定时器

(1)结构框图

        以通用定时器为例进行介绍,下面是其结构框图:

(2)定时器定时中断:

         (2.1)内部时钟驱动

以配置通用定时器TIM2为例,选取内部72MHz时钟作驱动,预分频7200,计数10000次,进行1s定时中断
步骤1:初始化TIM2时基单元
步骤2:清除中断标志位、设置中断类型
步骤3:设置NVIC开启中断
步骤4:启动定时器
*********************************************************************
void Tim2_ITR_Clk_init()//配置TIM2为1s定时中断,选取内部72MHz时钟作驱动,预分频7200,计数10000次,相当于72M/7200/10000=1Hz
{
	//初始化TIM2时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);     //开启TIM2外设的时钟
	TIM_InternalClockConfig(TIM2);                            //选择内部时钟作为TIM2时基单元的时钟(72MHz)
	
	TIM_TimeBaseInitTypeDef TIM_structrue;					  //定义TimeBase类型的结构体(配置时基单元)
	TIM_structrue.TIM_Prescaler			= 7200-1;			  //设置定时器预分频:7200分频(所选时钟经过预分频后才作为驱动时钟,设置0-65535)
	TIM_structrue.TIM_CounterMode       = TIM_CounterMode_Up; //计数模式:向上计数
	TIM_structrue.TIM_Period			= 10000-1;			  //设置自动重装值 :10000次(计满该值后重置,设置0-65535)
	TIM_structrue.TIM_ClockDivision		= TIM_CKD_DIV1;       //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_structrue.TIM_RepetitionCounter	= 0;				  //设置重复计数器:(该功能高级定时器才有,设0)
	TIM_TimeBaseInit(TIM2, &TIM_structrue);//时基单元初始化
	
	TIM_ClearFlag(TIM2, TIM_IT_Update);       //清除中断标志位(时基单元初始化时标志位会立刻置1需要清除,不然开启TIM2时立刻产生中断)
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//设置中断类型:选择定时器更新事件触发中断(计满时产生中断)
	
	//设置NVIC开启中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级组
	NVIC_InitTypeDef NVIC_structure;                              //定义NVIC类型的结构体
	NVIC_structure.NVIC_IRQChannel                   = TIM2_IRQn; //中断通道(不同芯片通道有区别,跳转定义查看) 
	NVIC_structure.NVIC_IRQChannelPreemptionPriority = 0;		  //设置抢占优先级为0
	NVIC_structure.NVIC_IRQChannelSubPriority		 = 0;		  //设置响应优先级为0
	NVIC_structure.NVIC_IRQChannelCmd				 = ENABLE;    //使能中断通道
	NVIC_Init(&NVIC_structure);//NVIC初始化函数

	//启动定时器
	TIM_Cmd(TIM2, ENABLE);
}
//定时器TIM2中断函数,(不用在头文件声明,函数名取startup_stm32f10x_md.s启动文件内对应中断函数名称)
//中断函数可写在mian函数或其他需要使用的位置:如中断内要操作mian函数内变量或使用其头文件内函数,就写在mian函数内
void TIM2_IRQHandler()//中断函数模板
{
	if( TIM_GetITStatus(TIM2, TIM_IT_Update)== SET)///再确定是TIM2更新事件产生的中断
	{
	    //在这里写中断函数
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清除待处理位,结束中断
	}
}

        (2.2)外部时钟驱动

配置TIM2定时中断,选取外部信号作驱动,这种情况相当于计数器,预分频1,计数3次(外部输入端口A0)
步骤1:初始化GPIO
步骤2:初始化TIM2时基单元
步骤3:清除中断标志位、设置中断类型
步骤4:设置NVIC开启中断
步骤5:启动定时器
*********************************************************************
void Tim2_ETR_Clk_init()//配置TIM2定时中断(选取外部时钟/信号作驱动)
{
	//初始化GPIOA的A0引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
   	GPIO_InitTypeDef GPIO_structrue;
	GPIO_structrue.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_structrue.GPIO_Mode  = GPIO_Mode_IPD;
	GPIO_structrue.GPIO_Pin   = GPIO_Pin_0;//TIM2的ETR引脚固定为A0(可参考手册)
	GPIO_Init(GPIOA, &GPIO_structrue);
	
	//初始化TIM2时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);     //开启TIM2外设的时钟
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); //选择外部时钟(信号)作为TIM2时基单元的时钟
															
	TIM_TimeBaseInitTypeDef TIM_structrue;					  //定义TimeBase类型的结构体(配置时基单元)
	TIM_structrue.TIM_Prescaler			= 1-1;			      //设置定时器预分频:1分频(所选时钟经过预分频后才作为驱动时钟,设置0-65535)
	TIM_structrue.TIM_CounterMode       = TIM_CounterMode_Up; //计数模式:向上计数
	TIM_structrue.TIM_Period			= 3-1;			      //设置自动重装值 :3(计满该值后重置,设置0-65535)
	TIM_structrue.TIM_ClockDivision		= TIM_CKD_DIV1;       //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_structrue.TIM_RepetitionCounter	= 0;				  //设置重复计数器:(该功能高级定时器才有,设0)
	TIM_TimeBaseInit(TIM2, &TIM_structrue);                   //时基单元初始化
	
	TIM_ClearFlag(TIM2, TIM_IT_Update);       //清除中断标志位(时基单元初始化时标志位会立刻置1需要清除,不然开启TIM2时立刻产生中断)
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//设置中断类型:选择定时器更新事件触发中断(计满时产生中断)
	
	//设置NVIC开启中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级组
	NVIC_InitTypeDef NVIC_structure;                              //定义NVIC类型的结构体
	NVIC_structure.NVIC_IRQChannel                   = TIM2_IRQn; //中断通道(不同芯片通道有区别,跳转定义查看) 
	NVIC_structure.NVIC_IRQChannelPreemptionPriority = 0;		  //设置抢占优先级为0
	NVIC_structure.NVIC_IRQChannelSubPriority		 = 0;		  //设置响应优先级为0
	NVIC_structure.NVIC_IRQChannelCmd				 = ENABLE;    //使能中断通道
	NVIC_Init(&NVIC_structure);//NVIC初始化函数

	//启动定时器
	TIM_Cmd(TIM2, ENABLE);

(3)输出比较模式 OC

        OC即(Output Compare)输出比较:每个高级定时器和通用定时器都拥有4个输出比较通道。配置好OC单元后,可通过比较CNT与CCR(捕获比较寄存器)值的关系,来对输出电平
进行置1、置0或翻转的操作,用于输出频率和占空比可调的PWM:

        下面例如OC单元选择PWM1模式时,计数值小于/大于CCR会输出高/低电平:

配置TIM2定时中断,选取外部信号作驱动,这种情况相当于计数器,预分频1,计数3次(外部输入端口A0)
步骤1:初始化GPIO
步骤2:初始化TIM2时基单元
步骤3:初始化OC配置输出比较单元
步骤4:启动定时器
*********************************************************************
void PWM_init()//PWM波初始化(TIM2的OC通道1产生PWM输出到A0,频率1KHz,占空比50%,分辨率1%)
{
	//初始化GPIOA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_Structure;
	GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Structure.GPIO_Mode  = GPIO_Mode_AF_PP; //复用推挽输出(参考手册)
	GPIO_Structure.GPIO_Pin   = GPIO_Pin_0;      //TIM2_CH1引脚固定为A0(参考手册)
	GPIO_Init(GPIOA, &GPIO_Structure);
	
	//初始化TIM2时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);     //开启TIM2外设的时钟
	TIM_InternalClockConfig(TIM2);                            //选择内部时钟作为TIM2时基单元的时钟(72MHz)
	TIM_TimeBaseInitTypeDef TIM_structrue;					  //定义TimeBase类型的结构体(配置时基单元)
	TIM_structrue.TIM_Prescaler			= 720-1;			  //设置预分频值PSC:720分频(所选时钟经过预分频后才作为驱动时钟,设置0-65535)
	TIM_structrue.TIM_CounterMode       = TIM_CounterMode_Up; //计数模式:向上计数
	TIM_structrue.TIM_Period			= 100-1;			  //设置自动重装值ARR:100次(计满该值后重置,设置0-65535)
	TIM_structrue.TIM_ClockDivision		= TIM_CKD_DIV1;       //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_structrue.TIM_RepetitionCounter	= 0;				  //设置重复计数器:(该功能高级定时器才有,设0)
	TIM_TimeBaseInit(TIM2, &TIM_structrue);                   //时基单元初始化
	
	//初始化OC配置输出比较单元
	TIM_OCInitTypeDef TIM_OC_structrue;                       //定义OC类型结构体变量(配置输出比较单元)
	TIM_OCStructInit(&TIM_OC_structrue); 					  //结构体初始化:若结构体内参数没全部用到时,最好执行此函数给所有成员赋默认值
	TIM_OC_structrue.TIM_OCMode      = TIM_OCMode_PWM1;		  //输出比较模式:通常选PWM模式1
	TIM_OC_structrue.TIM_OCPolarity  = TIM_OCPolarity_High;   //输出极性:选择为高(若选择低,则输出高低电平取反)
	TIM_OC_structrue.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	TIM_OC_structrue.TIM_Pulse       = 50-1;			      //初始的CCR值:捕获比较寄存器值,TIM计数会与CCR进行比较(设置0-65535)
	TIM_OC1Init(TIM2, &TIM_OC_structrue);					  //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1

	//启动定时器
	TIM_Cmd(TIM2, ENABLE);
}

TIM2设置CCR值:设置捕获比较寄存器1的值,用于控制PWM波的占空比
*********************************************************
    TIM_SetCompare1(TIM2, 要设置的值 );

(4)输入捕获模式 IC

        IC即(InputCaptrue)输入捕获,每个高级定时器和通用定时器都拥有4个输入捕获通道。当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数(测周法:如输入信号两个上升沿间隔一周期,通过两个上升沿锁存的CNT值关系计算频率)

        (4.1)输入捕获模式

输入捕获模式测输入PWM波频率(通过TIM3的通道1进行输入捕获,输入端口为A6)
步骤1:初始化GPIO
步骤2:初始化TIM3时基单元
步骤3:配置IC输入捕获通道(TIM_ICInit)
步骤4:设置从模式
步骤5:启动定时器
*********************************************************************
void IC_init() //输入捕获模式初始化
{
	//初始化GPIOA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_Structure;
	GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Structure.GPIO_Mode  = GPIO_Mode_IPU;   //上拉输入(参考手册推荐浮空输入,但为防止输入悬空不稳定选上拉)
	GPIO_Structure.GPIO_Pin   = GPIO_Pin_6;      //TIM3_CH1引脚固定为A6(参考手册)
	GPIO_Init(GPIOA, &GPIO_Structure);
	
	//初始化TIM2时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 , ENABLE);     //开启TIM3外设的时钟
	TIM_InternalClockConfig(TIM3);                            //选择内部时钟作为TIM2时基单元的时钟(72MHz)
	TIM_TimeBaseInitTypeDef TIM_structrue;					  //定义TimeBase类型的结构体(配置时基单元)
	TIM_structrue.TIM_Prescaler			= 72-1;		     	  //设置预分频值PSC:72分频(所选时钟经过预分频后才作为驱动时钟,设置0-65535)
	TIM_structrue.TIM_CounterMode       = TIM_CounterMode_Up; //计数模式:向上计数
	TIM_structrue.TIM_Period			= 65536-1;			  //设置自动重装值ARR:65535次最大值,满量程计数防止计数溢出
	TIM_structrue.TIM_ClockDivision		= TIM_CKD_DIV1;       //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_structrue.TIM_RepetitionCounter	= 0;				  //设置重复计数器:(该功能高级定时器才有,设0)
	TIM_TimeBaseInit(TIM3, &TIM_structrue);                   //时基单元初始化
	
	//配置IC输入捕获通道
	TIM_ICInitTypeDef TIM_ICstructrue;			      	     	//定义IC类型的结构体(配置IC输入捕获单元)
	TIM_ICstructrue.TIM_Channel		= TIM_Channel_1;  		  	//配置通道:通道1
	TIM_ICstructrue.TIM_ICFilter	= 0xF;  			      	//滤波参数:最大,可过滤信号抖动
	TIM_ICstructrue.TIM_ICPrescaler	= TIM_ICPSC_DIV1;         	//输入预分频:不分频
	TIM_ICstructrue.TIM_ICPolarity	= TIM_ICPolarity_Rising;  	//设置极性:上升沿触发捕获
	TIM_ICstructrue.TIM_ICSelection	= TIM_ICSelection_DirectTI;	//触发信号输入通道:直连通道
	TIM_ICInit(TIM3,&TIM_ICstructrue);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	
	//设置从模式(捕获后要将CNT清零)
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);		//设置触发源:TI1FP1(即捕获时直连通道处信号)
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);	//设置从模式:复位模式(计时器CNT复位)
	
	//启动定时器
	TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_GetFreq() //测周法测PWM波频率
{
	uint16_t CCR  = TIM_GetCapture1(TIM3);	//获取TIM3通道1的CCR寄存器值		
	//测周法求频率:F_input = F_TIM3 / CNT
	//IC模式下计时器在输入信号上升沿捕获CNT值至CCR,即一个输入信号周期有(CNT+1)个计时器驱动时钟周期(CNT)
	//注意待测频率不可以大于标准频率(甚至接近或0.1倍都不行)因为误差会很大
	return 1000000 / (CCR+1);
}

        (4.2)PWMI模式

        PWMI即PWM输入模式,是输入捕获模式特例。不同之处在于它使用TIM_PWMIConfig()进行输入捕获通道配置,将输入PWM波进行上沿和下沿,分别捕获到两个通道,可同时测量频率和占空比:

PWMI模式测输入PWM波频率、占空比(通过TIM3的通道1进行输入捕获,输入端口为A6)
步骤1:初始化GPIOA
步骤2:初始化TIM3时基单元
步骤3:配置IC输入捕获通道(TIM_PWMIConfig)
步骤4:设置从模式
步骤5:启动定时器
*********************************************************************
void IC_PWMI_init() //PWM输入模式初始化
{
	//初始化GPIOA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_Structure;
	GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Structure.GPIO_Mode  = GPIO_Mode_IPU;   //上拉输入(参考手册推荐浮空输入,但为防止输入悬空不稳定选上拉)
	GPIO_Structure.GPIO_Pin   = GPIO_Pin_6;      //TIM3_CH1引脚固定为A6(参考手册)
	GPIO_Init(GPIOA, &GPIO_Structure);
	
	//初始化TIM2时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 , ENABLE);     //开启TIM3外设的时钟
	TIM_InternalClockConfig(TIM3);                            //选择内部时钟作为TIM2时基单元的时钟(72MHz)
	TIM_TimeBaseInitTypeDef TIM_structrue;					  //定义TimeBase类型的结构体(配置时基单元)
	TIM_structrue.TIM_Prescaler			= 72-1;		     	  //设置预分频值PSC:72分频(所选时钟经过预分频后才作为驱动时钟,设置0-65535)
	TIM_structrue.TIM_CounterMode       = TIM_CounterMode_Up; //计数模式:向上计数
	TIM_structrue.TIM_Period			= 65536-1;			  //设置自动重装值ARR:65535次最大值,满量程计数防止计数溢出
	TIM_structrue.TIM_ClockDivision		= TIM_CKD_DIV1;       //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_structrue.TIM_RepetitionCounter	= 0;				  //设置重复计数器:(该功能高级定时器才有,设0)
	TIM_TimeBaseInit(TIM3, &TIM_structrue);                   //时基单元初始化
	
	//配置IC输入捕获通道
	TIM_ICInitTypeDef TIM_ICstructrue;			      	     	//定义IC类型的结构体(配置IC输入捕获单元)
	TIM_ICstructrue.TIM_Channel		= TIM_Channel_1;  		  	//配置通道:通道1
	TIM_ICstructrue.TIM_ICFilter	= 0xF;  			      	//滤波参数:最大,可过滤信号抖动
	TIM_ICstructrue.TIM_ICPrescaler	= TIM_ICPSC_DIV1;         	//输入预分频:不分频
	TIM_ICstructrue.TIM_ICPolarity	= TIM_ICPolarity_Rising;  	//设置极性:上升沿触发捕获
	TIM_ICstructrue.TIM_ICSelection	= TIM_ICSelection_DirectTI;	//触发信号输入通道:直连通道
	TIM_PWMIConfig(TIM3,&TIM_ICstructrue);	//将结构体变量交给TIM_PWMIConfig,配置IC输入捕获通道
											//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式	
	
	//设置从模式(捕获后要将CNT清零)
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);		//设置触发源:TI1FP1(即捕获时直连通道处信号)
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);	//设置从模式:复位模式(计时器CNT复位)
	
	//启动定时器
	TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_PWMI_GetFreq() //测周法测PWM波频率
{
	uint16_t CCR  = TIM_GetCapture1(TIM3);	//获取TIM3通道1的CCR寄存器值	
	return 1000000 / (CCR+1);
}

uint32_t IC_PWMI_GetDuty() //测占空比(百分制%)
{
	uint16_t CCR1  = TIM_GetCapture1(TIM3);	//获取TIM3通道1的CCR寄存器值	
	uint16_t CCR2  = TIM_GetCapture2(TIM3);	//获取TIM3通道2的CCR寄存器值	
	return (CCR2+1)*100 / (CCR1+1);			//求占空比(百分制%):D=(CCR2+1)*100 / (CCR1+1)
}

(5)编码器接口模式

        Encoder Interface 编码器接口,可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度。

        每个高级定时器和通用定时器都拥有1个编码器接口,两个输入引脚借用了输入捕获的通道1和通道2。(详细内容可查看参考手册)

获取旋转编码器的方向与转速(选取TIM3,编码器输入接口A、B对应A6、A7)
步骤1:初始化GPIO
步骤2:初始化TIM3时基单元
步骤3:配置IC输入捕获通道(只配置通道、输入滤波)
步骤4:配置编码器接口模式(TIM_EncoderInterfaceConfig)
步骤5:启动定时器
*********************************************************************
void Encoder_Interface_init()//编码器接口模式初始化
{
	//初始化GPIOA
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_Structure;
	GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Structure.GPIO_Mode  = GPIO_Mode_IPU;   	     //上拉输入(参考手册推荐浮空输入,但为防止输入悬空不稳定选上拉)
	GPIO_Structure.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7; //TIM3_CH1和CH2引脚固定为A6和A7(参考手册)
	GPIO_Init(GPIOA, &GPIO_Structure);
	
	//初始化TIM3时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    //TIM_InternalClockConfig(TIM3);                          //时钟选择:被编码器接口托管,时钟即为两输入接口,不用设置
	TIM_TimeBaseInitTypeDef TIM_structrue;					  //定义TimeBase类型的结构体(配置时基单元)
	TIM_structrue.TIM_Prescaler			= 1-1;			      //时钟预分频:1分频(时钟经预分频后作为驱动时钟,设置0-65535)
	//TIM_structrue.TIM_CounterMode     =            		  //计数模式:被编码器接口托管,不用设置
	TIM_structrue.TIM_Period			= 65536-1;			  //设置自动重装值 :65535次(计满该值后重置,设置0-65535)
	TIM_structrue.TIM_ClockDivision		= TIM_CKD_DIV1;       //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_structrue.TIM_RepetitionCounter	= 0;				  //设置重复计数器:(该功能高级定时器才有,设0)
	TIM_TimeBaseInit(TIM3, &TIM_structrue);                   //时基单元初始化
	
	//配置IC输入捕获通道(只用到了1、2通道)
	TIM_ICInitTypeDef TIM_ICstructrue;			      	     	//定义IC类型的结构体(配置IC输入捕获单元)
	TIM_ICStructInit(&TIM_ICstructrue);			      	     	//初始化结构体为默认值(因为编码器接口模式下IC输入捕获通道不用配置全)
	TIM_ICstructrue.TIM_Channel		= TIM_Channel_1;  		  	//配置通道:通道1
	TIM_ICstructrue.TIM_ICFilter	= 0xF;  			      	//滤波参数:最大,可过滤信号抖动
	TIM_ICInit(TIM3,&TIM_ICstructrue);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	
	TIM_ICstructrue.TIM_Channel		= TIM_Channel_2;  		  	//照搬1的结构配置体快速配置IC通道2	
	TIM_ICInit(TIM3,&TIM_ICstructrue);							
	
	//配置编码器接口模式
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);	
	
	//启动定时器
	TIM_Cmd(TIM3, ENABLE);
}

uint16_t Encoder_Interface_GetCNT()//获取当前计时器CNT值
{
	return TIM_GetCounter(TIM3);
}

int16_t Encoder_Interface_GetSpeed()//获取CNT值并清零(反复执行该函数时,CNT代表速度,为执行间隔时间段的平均速度)
									//可用延时或定时中断(推荐)反复执行
{
	int16_t CNT = TIM_GetCounter(TIM3);//CNT转为带符号数据
	TIM_SetCounter(TIM3, 0);
	return CNT;
}

四、ADC

        STM32F103C8T6 配有ADC1、ADC2,一共10个外部输入通道,包括8个外部输入和2个内部输入(内部温度传感器、内部参考电压)。 

        ADC为12位逐次逼近型ADC,输入电压范围:0~3.3V,转换范围:0~4095(2^12-1)

(1)结构框图

(2)单通道AD转换

        下面给出2个单通道输入模拟电压进行ADC转换的例子,区别主要在于是否为连续转换模式:

例1:单通道A0输入,采用规则组、软件触发、非扫描模式、单次转换的方式进行数模转换
步骤1:初始化GPIO
步骤2:初始化ADC
步骤3:开启ADC
步骤4:ADC校准
*********************************************************************
void ADC_A0_init()//ADC初始化,单通道A0输入,采用规则组、软件触发、非扫描模式、单次转换的方式进行
{
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_structure;
	GPIO_structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_structure.GPIO_Mode  = GPIO_Mode_AIN; //ADC用模拟输入模式
	GPIO_structure.GPIO_Pin   = GPIO_Pin_0;    //ADC1 通道0对应A0脚
	GPIO_Init(GPIOA, &GPIO_structure);
	
	//初始化ADC
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  		     //开启ADC外设时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);				      			 //ADC驱动时钟分频:6分频(72MHz/6=12MHz)
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//通道配置:通道0配置到规则组,组内第1个采样、采样时间55.5个ADC周期
	ADC_InitTypeDef ADC_structure;                                   //定义ADC类型结构体
	ADC_structure.ADC_Mode               = ADC_Mode_Independent; 	 //ADC模式:独立模式
	ADC_structure.ADC_DataAlign          = ADC_DataAlign_Right;  	 //数据对齐:右对齐(右对齐直接是结果值)
	ADC_structure.ADC_NbrOfChannel		 = 1;                        //通道数目:1
	ADC_structure.ADC_ScanConvMode		 = DISABLE;              	 //扫描模式:关(仅1个通道,不需要扫描通道依次转换)
	ADC_structure.ADC_ExternalTrigConv	 = ADC_ExternalTrigConv_None;//外部触发源:无(软件触发,不用外部触发源)
	ADC_structure.ADC_ContinuousConvMode = DISABLE;                  //连续转换模式:关(即单次转换,软件触发一次转换一次)
	ADC_Init(ADC1, &ADC_structure);
	
	//开启ADC
	ADC_Cmd(ADC1, ENABLE);
	
	//ADC校准(F10系列手册建议ADC开启后做一次校准)
	ADC_ResetCalibration(ADC1); 					   //复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET); //等待复位完毕(复位完毕硬件自动清零)
	ADC_StartCalibration(ADC1); 					   //开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);      //等待校准完毕(校准完毕硬件自动清零)
}

uint16_t ADC_A0_GetValue()//读取转换值,范围为0~4095(软件触发)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换(获取标志位:规则组结束标志位)
	ADC_ClearFlag(ADC1, ADC_FLAG_EOC);                    //清除标志位:规则组结束标志位
	return ADC_GetConversionValue(ADC1);                  //读取数据时自动清除EOC标志位,所以不用手动清除
}
float ADC_A0_GetVoltage()//读取转换值,并换算为电压,范围为0~3.3V(软件触发)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换(获取标志位:规则组结束标志位)
	ADC_ClearFlag(ADC1, ADC_FLAG_EOC);                    //清除标志位:规则组结束标志位
	uint16_t D = ADC_GetConversionValue(ADC1);            //读取数据时自动清除EOC标志位,所以不用手动清除
	float V    = (float)D /4095*3.3;	  //换算电压
	return V;                  
}
例2:单通道A0输入,采用规则组、软件触发、非扫描模式、**连续转换**的方式进行数模转换
步骤1:初始化GPIO
步骤2:初始化ADC
步骤3:开启ADC
步骤4:ADC校准
步骤5:软件触发一次,自动开始连续转换
*********************************************************************
void ADC_A0_init()//ADC初始化
{
	//初始化GPIO
    //通道配置
	//初始化ADC
	ADC_structure.ADC_ContinuousConvMode = ENABLE;//连续转换模式:开(即一直转换,结果数据不停更新,需要时读取即可)
	//开启ADC
	//ADC校准
    除ADC初始化开启连续转换模式外,前5步同上

    //软件触发,开始连续转换
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	
}

uint16_t ADC_A0_GetValue()//读取转换值,范围为0~4095
{
	return ADC_GetConversionValue(ADC1);                  //读取数据时自动清除EOC标志位,所以不用手动清除
}
float ADC_A0_GetVoltage()//读取转换值,并换算为电压,范围为0~3.3V
{
	uint16_t D = ADC_GetConversionValue(ADC1);            //读取数据时自动清除EOC标志位,所以不用手动清除
	float V    = (float)D /4095*3.3;	                  //换算电压
	return V;                  
}

(3)多通道AD转换

        下面给出2个多通道输入模拟电压进行ADC转换的例子

例1:多通道A0123输入,采用规则组、软件触发、非扫描模式、单次转换的方式进行数模转换
步骤1:初始化GPIO
步骤2:初始化ADC(通道配置放在读取数据函数中,要哪个ADC数据就选哪个通道进行转换读取)
步骤3:开启ADC
步骤4:ADC校准
*********************************************************************
void ADC_A0123_init()//ADC初始化,多通道A0123输入,采用规则组、软件触发、非扫描模式、单次转换的方式进行数模转换
{
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_structure;
	GPIO_structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_structure.GPIO_Mode  = GPIO_Mode_AIN; //ADC用模拟输入模式
	GPIO_structure.GPIO_Pin   = GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3; //ADC1通道0123对应A0123脚
	GPIO_Init(GPIOA, &GPIO_structure);
	
	//初始化ADC
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  			 //开启ADC外设时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);				      			 //ADC驱动时钟分频:6分频(72MHz/6=12MHz)
	ADC_InitTypeDef ADC_structure;                                   //定义ADC类型结构体
	ADC_structure.ADC_Mode               = ADC_Mode_Independent; 	 //ADC模式:独立模式
	ADC_structure.ADC_DataAlign          = ADC_DataAlign_Right;  	 //数据对齐:右对齐(右对齐直接是结果值)
	ADC_structure.ADC_NbrOfChannel		 = 1;                        //通道数目:1
	ADC_structure.ADC_ScanConvMode		 = DISABLE;              	 //扫描模式:关(仅1个通道,不需要扫描通道依次转换)
	ADC_structure.ADC_ExternalTrigConv	 = ADC_ExternalTrigConv_None;//外部触发源:无(转换用软件触发,不用外部触发源)
	ADC_structure.ADC_ContinuousConvMode = DISABLE;                  //连续转换模式:关(即单次转换,软件触发一次转换一次)
	ADC_Init(ADC1, &ADC_structure);
	
	//开启ADC
	ADC_Cmd(ADC1, ENABLE);
	
	//ADC校准(F10系列手册建议ADC开启后做一次校准)
	ADC_ResetCalibration(ADC1); 					   //复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET); //等待复位完毕(复位完毕硬件自动清零)
	ADC_StartCalibration(ADC1); 					   //开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);      //等待校准完毕(校准完毕硬件自动清零)
}

uint16_t ADC_A0123_GetValue(uint16_t ADC_Channel)//读取转换值,范围为0~4095(软件触发)
{
	//通道配置:通道0配置到规则组,组内第1个采样、采样时间55.5个ADC周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待转换(获取标志位:规则组结束标志位)
	ADC_ClearFlag(ADC1, ADC_FLAG_EOC);                    //清除标志位:规则组结束标志位
	return ADC_GetConversionValue(ADC1);                  //读取数据时自动清除EOC标志位,所以不用手动清除
}
例2:多通道A0123输入,采用规则组、软件触发、扫描模式、连续转换的方式进行数模转换,
ADC数据通过DMA自动进行转运存储,不需要调用函数读取,可直接用数据:详见五、DMA
*********************************************************************

五、DMA

        DMA(Direct Memory Access)直接存储器访问,它允许外部设备(如ADC、DAC、USART等)直接访问内存,无须CPU干预,数据可通过DMA快速转运,节省了CPU资源来做其他操作。

        两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

(1)软件触发DAM

        软件触发通过调用函数进行DMA数据转运,实现寄存器数据转运到其他寄存器

实现数组A数据对应转运给B,AB内数据个数均为4
步骤1:初始化DMA
步骤2:关闭DMA(如果开启会立刻转运一次)
*********************************************************************
static uint32_t DataSize;//定义静态全局变量:数据个数
//DMA初始化:软件触发DMA,将uint8_t数组数据A传输给数组B,AB内数据个数为Size(因为是数组所以数据地址都是自增的)
void DMA_M2M_init(uint32_t AddrA, uint32_t AddrB, uint32_t Size)
{
	DataSize = Size;//把数据个数给全局变量DataSize,以供DMA_M2M_transfer函数使用
	
	//初始化DMA
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_InitTypeDef DAM_structure;
	DAM_structure.DMA_PeripheralBaseAddr = AddrA;						//外设地址:AddrA(“外设”只是名字,并不是外设,即存储器1)     
	DAM_structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//外设数据宽度:字节数据 uint8_t
	DAM_structure.DMA_PeripheralInc      = DMA_PeripheralInc_Enable;   	//外设地址自增:是
	DAM_structure.DMA_MemoryBaseAddr     = AddrB;						//存储器地址: AddrB(即存储器2)
	DAM_structure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;     //存储器数据宽度:字节数据 uint8_t
	DAM_structure.DMA_MemoryInc          = DMA_MemoryInc_Enable;        //存储器地址自增:是
	DAM_structure.DMA_DIR                = DMA_DIR_PeripheralSRC;       //传输方向:外设(存储器1)作为源
	DAM_structure.DMA_BufferSize         = Size;                        //数据个数:Size(即传输数据的个数,数据个数计数器,转运结束会减到0)
	DAM_structure.DMA_M2M                = DMA_M2M_Enable;				//存储器到存储器:是(即软件触发DMA)
	DAM_structure.DMA_Mode               = DMA_Mode_Normal;             //传输模式:正常模式(单次传输不循环)
	DAM_structure.DMA_Priority           = DMA_Priority_Medium;         //优先级:中等(DMA多个通道同时传输时的优先级)
	DMA_Init(DMA1_Channel1, &DAM_structure);
	
	//关闭DMA
	DMA_Cmd(DMA1_Channel1, DISABLE);//初始化后如果开启DMA会立即进行一次数据转运,故设计为调用DMA_M2M_transfer()再开始
}

void DMA_M2M_transfer()//再次将AB数组数据进行转运
{
	DMA_Cmd(DMA1_Channel1, DISABLE);				 //关闭DMA(数据个数计数器赋值需要先失能再赋值)
	DMA_SetCurrDataCounter(DMA1_Channel1, DataSize); //将数据个数重新填入数据个数计数器
	DMA_Cmd(DMA1_Channel1, ENABLE);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//等待转运结束(等到转运结束标志位SET置起退出循环)
	DMA_ClearFlag(DMA1_FLAG_TC1);                    //手动清除标志位
}

(2)硬件触发DAM

         硬件触发即当外设发出请求DMA时自动进行DMA数据转运

多通道A0123输入,采用规则组、软件触发、扫描模式、连续转换的方式进行数模转换,
ADC数据通过DMA自动进行转运存储,不需要调用函数读取,可直接用数据
步骤1:初始化GPIO
步骤2:初始化ADC
步骤3:初始化DMA
步骤4:开启ADC、开启ADC的DAM请求、开启DMA
步骤5:ADC校准
步骤6:软件触发ADC(ADC开始连续转换)
*********************************************************************
uint16_t ADC_Value[4];//定义一个数组用于存放DMA读取到的ADC转换值(对应A0123)
//ADC初始化,多通道A0123输入,采用规则组、软件触发、扫描模式、连续转换的方式进行。初始化后,ADC数据通过DMA自动进行转运存储
void ADC_DMA_init()
{
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_structure;
	GPIO_structure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_structure.GPIO_Mode  = GPIO_Mode_AIN; //ADC用模拟输入模式
	GPIO_structure.GPIO_Pin   = GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3; //ADC1通道0123对应A0123脚
	GPIO_Init(GPIOA, &GPIO_structure);
	
	//初始化ADC
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  		     //开启ADC外设时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);				      			 //ADC驱动时钟分频:6分频(72MHz/6=12MHz)
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//通道配置:通道0123配置到规则组,分别为组内第1234个采样、采样时间55.5个ADC周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);												
	ADC_InitTypeDef ADC_structure;                                   //定义ADC类型结构体
	ADC_structure.ADC_Mode               = ADC_Mode_Independent; 	 //ADC模式:独立模式
	ADC_structure.ADC_DataAlign          = ADC_DataAlign_Right;  	 //数据对齐:右对齐(右对齐直接是结果值)
	ADC_structure.ADC_NbrOfChannel		 = 4;                        //通道数目:4
	ADC_structure.ADC_ScanConvMode		 = ENABLE;              	 //扫描模式:开(4个通道,扫描通道依次转换)
	ADC_structure.ADC_ExternalTrigConv	 = ADC_ExternalTrigConv_None;//外部触发源:无(软件触发,不用外部触发源)
	ADC_structure.ADC_ContinuousConvMode = ENABLE;                   //连续转换模式:开(软件触发一次后开始循环转换)
	ADC_Init(ADC1, &ADC_structure);
	
	//初始化DMA
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_InitTypeDef DAM_structure;
	DAM_structure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;			//外设地址:ADC规则数据寄存器结果寄存器ADC_DR(即规则组转换结果存储寄存器,这里用指针并转为32位,也可写0x4001244C)
	DAM_structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据宽度:半字数据 uint16_t(寄存器低16位)
	DAM_structure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;   //外设地址自增:否(ADC规则数据寄存器只有一个,其值随转换不断刷新)
	DAM_structure.DMA_MemoryBaseAddr     = (uint32_t)ADC_Value;			//存储器地址: ADC_Value(即存储器2)
	DAM_structure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord; //存储器数据宽度:半字数据 uint16_t
	DAM_structure.DMA_MemoryInc          = DMA_MemoryInc_Enable;        //存储器地址自增:是
	DAM_structure.DMA_DIR                = DMA_DIR_PeripheralSRC;       //传输方向:外设(存储器1)作为源
	DAM_structure.DMA_BufferSize         = 4;                           //数据个数:4(即传输数据的个数,这里对应4个通道)
	DAM_structure.DMA_M2M                = DMA_M2M_Disable;				//存储器到存储器:否(即硬件触发DMA)
	DAM_structure.DMA_Mode               = DMA_Mode_Circular;           //传输模式:传输模式(循环传输,自动重装数据个数计数器)
	DAM_structure.DMA_Priority           = DMA_Priority_Medium;         //优先级:中等(DMA多个通道同时传输时的优先级)
	DMA_Init(DMA1_Channel1, &DAM_structure);
	
	//开启ADC、开启ADC的DAM请求、开启DMA
	ADC_Cmd(ADC1, ENABLE);
	ADC_DMACmd(ADC1, ENABLE);//ADC1的DMA请求只给DMA通道1(参考手册),因此前面DMA通道必须初始化通道1
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//ADC校准(F10系列手册建议ADC开启后做一次校准)
	ADC_ResetCalibration(ADC1); 					   //复位校准
	while(ADC_GetResetCalibrationStatus(ADC1) == SET); //等待复位完毕(复位完毕硬件自动清零)
	ADC_StartCalibration(ADC1); 					   //开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET);      //等待校准完毕(校准完毕硬件自动清零)
	
	//软件触发ADC(连续转换需要软件触发1次才自动连续转换)
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	
}

待更。。。。

猜你喜欢

转载自blog.csdn.net/2303_76814451/article/details/143604996
今日推荐