stm32(F103c8t6)自学笔记@阿布君

学习过程中的注意点:

1.注意头文件和C文件的包含关系,C文件自身应包含自身的H头文件以及用到的外部头文件,而自身头文件只需包含头文件需要用到的资源文件即可。

参考文献:http://t.csdn.cn/o2GmL

一、认识STM32

1.简介

2.命名规则  

3.系统结构

4.引脚&功能

注释:

红色表示:与电源相关

蓝色是最小系统相关的引脚

绿色是IO口、功能口

S代表电源、I代表输入、O代表输出、IO代表输入输出、FT代表能容忍5v电压(没有就是3.3v)

芯片上小黑点旁为第一个IO口,逆时针增加;

5.启动配置 (BOOT三种模式

6.实物电路连接及Keil的设置 (ST-Link)

二、软件环境安装(MDK)及新建工程

1.MDK的安装

stm32不同于C51,需要安装ARM_mdk的版本;

keil5及之后的版本需要安装芯片对应系列的器件库(pack);

stm的编程模式除了像单片机那样(1)直接操作寄存器(更可靠但是复杂、麻烦),还有通过(2)使用官方封装好的库函数,还有一种方法是(3)使用Hal库(如使用STM32CubeMX软件实现自动初始化配置)。

安装过程不在过多赘述,CSDN上有实现C51、ARM、C251三版共存的方法。

2.新建工程

因为采用的是使用官方封装好的库函数,所以并不像之前配置单片机那样去简单的只插入头文件然后写程序就好,而是先导入官方的启动文件,搭建环境。

2.1 配置成寄存器开发模式(工程)(详细步骤)

1.建立启动文件夹start,导入启动文件

启动文件
必要头文件
内核的寄存器描述文件以及配置函数库
成为这样

        这里注意:上图中的md.s后缀文件是根据单片机型号选择的,参考下图

2.建立用户文件夹User

        创建main.c

3.完成

2.1.1 寄存器开发模式下实现点亮LED(GPIO13)

1.首先打开RCC寄存器的APB2使能GPIOC的时钟

2.再配置对应的GPIOC的模式

3.再写入该GPIOC的输出数据

        灯是低电平点亮,高电平熄灭

4.完成

5.总结收获

通过该方法配置很繁琐,且每次配置会影响其它IO口的数据,只能通过像单片机中的&=、|=来解决,更麻烦。

2.2 配置成标准库开发模式(工程)(详细步骤)

1.在寄存器开发模板的基础上,新建Lirary文件夹,导入c文件、头文件

         全部粘贴到刚建立的文件夹Library

        将这三个粘贴到User文件夹(下面需要在Keil里配置路径)

2.在Keil里添加上述文件,配置Keil

一定要定义 USE_STDPERIPH_DRIVER

        USE使用、下划线、STD标准、PERIPH外设、下划线、DRIVER驱动

自己定义的带有头文件的文件夹都需要添加入路径

 3.完成!

编译0错误0警告

2.2.1 标准库开发模式下实现点亮LED(GPIO13)

配置思路和配置库函数一样,不同在于不用查手册看具体位以及担心改变其它位的数据了

操作流程还是一样的

1.使能RCC寄存器控制的APB2总线上的外设IO时钟

2.配置目标外设的模式

        这里注意:由于库函数定义的GPIO_Init函数需要传入一个结构体变量的地址,这里是将需要配置的 Pin口、Speed传输速度、Mode输出模式 三个封装在了结构体里以此简化了代码。我们调用前必须要先自定义该结构体类型的变量

3.配置GPIOC_Pin口输出的数据

    GPIO_SetBits (GPIOC,GPIO_Pin_13);  //输出高电平(熄灭)
    GPIO_ResetBits (GPIOC,GPIO_Pin_13);  //输出低电平(点亮)

#include "stm32f10x.h"                  // Device header


int main(void)
{
//	RCC->APB2ENR = 0X00000010;  //配置寄存器实现点灯
//	GPIOC->CRH = 0X00300000;
//	GPIOC->ODR = 0X00000000;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能GPIOC时钟
	GPIO_InitTypeDef GPIO_InitStruct;  //定义的结构体
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  //输出模式
	GPIO_InitStruct .GPIO_Pin = GPIO_Pin_13;  //输出IO口
	GPIO_InitStruct .GPIO_Speed = GPIO_Speed_50MHz;  //输出速度
	GPIO_Init(GPIOC,&GPIO_InitStruct);  //配置GPIOC的模式,这里将三种模式放在了一个结构体内,因此要在上面定义一个结构体
	//GPIO_SetBits (GPIOC,GPIO_Pin_13);  //输出高电平(熄灭)
	GPIO_ResetBits (GPIOC,GPIO_Pin_13);  //输出低电平(点亮)
	while(1)
	{
		
	}
}

3.工程架构

4.步骤回顾、总结

三、GPIO(外设)

1.GPIO结构及原理

1.1 介绍

1.2 结构 

简单来说,GPIO是受RCC寄存器 控制的APB2总线上的一系列外设IO口。

这些IO口由 GPIO A~GPIO E ,而单个GPIO如GPIOC都是又下分为16个小IO口(引脚),其中驱动器是为了增加引脚电平驱动能力

单个外设口如GPIOC的内部电路结构如下:

        下图中的肖特基触发器是文献翻译错误,实际应为施密特触发器。

 施密特触发器原理:

        内部可设两个阈值,输入信号高于上限为高电平,低于下限为低电平

1.3 GPIO的八种模式

typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

Stm32库里将8种模式定义为了枚举类型

  • GPIO_Mode_AIN:模拟输入模式(Analog Input Mode)。
  • GPIO_Mode_IN_FLOATING:浮空输入模式(Floating Input Mode)。
  • GPIO_Mode_IPD:下拉输入模式(Input Pull-down Mode)。
  • GPIO_Mode_IPU:上拉输入模式(Input Pull-up Mode)。
  • GPIO_Mode_Out_OD:开漏输出模式(Output Open-drain Mode)。
  • GPIO_Mode_Out_PP:推挽输出模式(Output Push-pull Mode)。
  • GPIO_Mode_AF_OD:复用开漏输出模式(Alternate Function Output Open-drain Mode)。
  • GPIO_Mode_AF_PP:复用推挽输出模式(Alternate Function Output Push-pull Mode)。

2.GPIO输出

2.1 可输出设备(LED、蜂鸣器...)

和51单片机一样,每个IO口都具有输出功能(高低电平)

因此可实现点亮LED,蜂鸣器报警等功能。

2.2Stm32中LED、蜂鸣器的电路

2.3 面包板(结构)

2.4 实现LED闪烁(含代码)

一定要先按照自己的思路和理解去写一遍,主要就是调用各种库函数。

下面代码需要注意的是:

GPIO_InitTypeDef A,B,C;  //定义三个IO的结构体变量 A是PA0口、B是PA1口、C是PA3口

这段代码如果在函数中间定义会报错,那么是由于你的Keil版本过低导致,按下图设置:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );//使能GPIO时钟
	
	GPIO_InitTypeDef A,B,C;  //定义三个GPIO的结构体变量 A是GPIOA、B是GPIOB、C是GPIOC//这里理解也不对,因为ABC只在RCC那里使能打开,这里定义的只是变量名,只是一个中间媒介去实现传值

	A.GPIO_Mode=GPIO_Mode_Out_PP;//设置为推挽输出模式
	A.GPIO_Pin=GPIO_Pin_0;  //设置为Pin0口
	A.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz
	GPIO_Init( GPIOA,&A); //以上三个都要传入该函数初始化
	
	B.GPIO_Mode=GPIO_Mode_Out_PP;
	B.GPIO_Pin=GPIO_Pin_1;
	B.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init( GPIOA,&B);
	
	C.GPIO_Mode=GPIO_Mode_Out_PP;
	C.GPIO_Pin=GPIO_Pin_2;
	C.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init( GPIOA,&C);
	while(1)
	{
		GPIO_ResetBits (GPIOA,GPIO_Pin_0);//先亮
		GPIO_ResetBits (GPIOA,GPIO_Pin_1);
		GPIO_ResetBits (GPIOA,GPIO_Pin_2);
		Delay_ms(500);
		GPIO_SetBits (GPIOA,GPIO_Pin_0);//再灭
		GPIO_SetBits (GPIOA,GPIO_Pin_1);
		GPIO_SetBits (GPIOA,GPIO_Pin_2);
		Delay_ms(500);
	}
}

不知道你是否发现,其实代码里是定义了三个外设,每个外设又单独设置的IO,其实这种方法是不对的,也是初学时容易产生的误区,如何修改,看下面的代码。

2.5 实现LED流水灯(含代码、理解

 初始化时是初始化一个外设,然后一个外设又有16个引脚。

上面的代码中 GPIO_InitTypeDef A,B,C;  //定义三个GPIO的结构体变量 A是GPIOA、B是GPIOB、C是GPIOC

这里理解也不对,因为ABC只在RCC寄存器那里使能打开,这里定义的只是变量名,只是一个中间媒介去实现传值

个人觉得这样定义可以实现一个外设例如GPIOA的16个引脚可以设置为不同的输出模式。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );//使能GPIO时钟
	
	GPIO_InitTypeDef A;  //定义一个中间结构体变量

	A.GPIO_Mode=GPIO_Mode_Out_PP;//设置为推挽输出模式
	A.GPIO_Pin=GPIO_Pin_All;  //设置所有的Pin口(PA0~PA15)
	A.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz
	GPIO_Init( GPIOA,&A); //以上三个都要传入该函数初始化
	
	while(1)
	{
		unsigned char i;
		for(i=0;i<8;i++)
		{
			GPIO_Write(GPIOA ,~(0X0001<<i));//0000 0000 0000 0001依次左移8位 该函数可以整体写入GPIOx->ODR
			Delay_ms(100);
		}
	}
}

2.6 蜂鸣器(含代码)

 配置原理和配LED是一样的

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE );//使能GPIO时钟
	
	GPIO_InitTypeDef A;

	A.GPIO_Mode=GPIO_Mode_Out_PP;//设置为推挽输出模式
	A.GPIO_Pin=GPIO_Pin_12;  //设置为Pin0口
	A.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz
	GPIO_Init( GPIOB,&A); //以上三个都要传入该函数初始化
	while(1)
	{
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);//低电平触发
		Delay_ms (100);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);//不响
		Delay_ms (100);
	}
}

3.GPIO输入

3.1 可输入设备(按键、温湿度...)

3.2 Stm32中按键、红外发射等器件的电路

3.3 复习数据类型、宏定义、Typedef、结构体、枚举

 define能改变任何数据的名字,但是编译器不会检测是否有错

 Typedef结构体相关的用法和说明可以看我的另外一篇文章

http://t.csdn.cn/H8ikF

枚举类似于结构体,其中关键词 enum相当于结构体的关键词 struct

注意enum中是用逗号分隔;

枚举的作用就是限制变量值的输入取值范围,就像星期几只能从七天里选一个;

取值超出范围会报错;

下附释义代码

#include <stdio.h>

int main()
{
	typedef enum{ 
		mon=1,
		tur, //	等效于mon=1,tur=2,wen=3,tue=4
		wen,
		tue
	}day;//这里将整个枚举改名为day 
	day a,b,c;
	int d;
	
	a=tur;
	b=(day)1;//直接赋值int型不行 ,需要转化为枚举型 
	c=day(4);
	
	d=wen;//外界数据也可以引用枚举集合里的数 
	
	printf("a=%d\n",a);
	printf("b=%d\n",b);
	printf("c=%d\n",c);
	printf("d=%d\n",d);
 } 

3.4 代码(按键控制LED)(封装)(Delay函数封装)

Stm32中延时函数Delay的us、ms、s的代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"

/***************************************************/延时函数模块
#include "Delay.h"
#include "stm32f10x.h" 

/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

/**
  * @brief  毫秒级延时
  * @param  xms 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
 
/**
  * @brief  秒级延时
  * @param  xs 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 

/***************************************************/LED模块
#include "LED.h"
#include "stm32f10x.h"                  // Device header

void LED_init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );//使能GPIOA的时钟
	
	GPIO_InitTypeDef A;  //定义两个IO的结构体变量 A是PA0口、B是PB0口

	A.GPIO_Mode=GPIO_Mode_Out_PP;//设置为推挽输出模式
	A.GPIO_Pin=GPIO_Pin_All;  //设置为所有Pin口
	A.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz
	GPIO_Init( GPIOA,&A); //以上三个都要传入该函数初始化
	
}

void LED_state(uint16_t GPIO_Pin,FunctionalState NewState)//指定Pin口和对应状态
{
	if(NewState==0)
		GPIO_ResetBits(GPIOA,GPIO_Pin);//置低电平
	else
		GPIO_SetBits(GPIOA,GPIO_Pin);//置高电平
}

/***************************************************/按键模块
#include "Key.h"                  // Device header
#include "Delay.h"


void Key_init(uint16_t GPIO_Pin)//需要指定端口Pin进行模式设置
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE );//使能GPIOA的时钟
	
	GPIO_InitTypeDef A;  //定义两个IO的结构体变量 A是PA0口、B是PB0口

	A.GPIO_Mode=GPIO_Mode_IPU;//设置为 上拉输入 模式
	A.GPIO_Pin=GPIO_Pin;  //外界参数设置Pin口
	A.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz,在输入模式下该设置没用
	GPIO_Init( GPIOB,&A); //以上三个都要传入该函数初始化
}

uint8_t get_keynum(uint16_t GPIO_Pin)
{
	uint8_t keynum;
	keynum =GPIO_ReadInputDataBit(GPIOB,GPIO_Pin);
	if(keynum ==0)
	{
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin) ==0);
		Delay_ms(10);
		return 0;
	}
	else 
		return keynum ;
}

/***************************************************/主函数
int main(void)
{
	uint8_t state=0;
	LED_init();
	Key_init(GPIO_Pin_12);
	while(1)
	{
		if(get_keynum(GPIO_Pin_12) ==0)
		{
			state ++;
		}
		if(state %2)
		{
			LED_state(GPIO_Pin_All ,ENABLE);
		}
		else 
		{
			LED_state(GPIO_Pin_All,DISABLE);
		}
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

3.5 代码(光敏电阻控制蜂鸣器及LED)(封装)

#include "stm32f10x.h"                  // Device header
#include "Buzzer.h"
#include "LightR.h"
#include "LED.h"


/***************************************************/蜂鸣器模块
#include "Buzzer.h"                  // Device header
#include "stm32f10x.h" 

GPIO_InitTypeDef buzzer;  

void Buzzer_init(uint16_t GPIO_Pin)//需要指定端口Pin进行模式设置
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE );//使能GPIOB的时钟
	
	

	buzzer.GPIO_Mode=GPIO_Mode_Out_PP;//设置为推挽输出模式
	buzzer.GPIO_Pin=GPIO_Pin;  //外界参数设置Pin口
	buzzer.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz,在输入模式下该设置没用
	GPIO_Init( GPIOB,&buzzer); //以上三个都要传入该函数初始化
}

void Buzzer_run(unsigned char Flag)//设定运行标志位
{
	if(Flag==0)
		GPIO_ResetBits(GPIOB,buzzer.GPIO_Pin);//置低电平
	else
		GPIO_SetBits(GPIOB,buzzer.GPIO_Pin);//置高电平
}

/***************************************************/光敏电阻模块
#include "LightR.h"
#include "stm32f10x.h" 

GPIO_InitTypeDef lightr;  

void LightR_init(uint16_t GPIO_Pin)//需要指定端口Pin进行模式设置
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE);//使能GPIOB的时钟
	
	

	lightr.GPIO_Mode=GPIO_Mode_IPU;//设置为 上拉输入 模式
	lightr.GPIO_Pin=GPIO_Pin;  //外界参数设置Pin口
	lightr.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz,在输入模式下该设置没用
	GPIO_Init( GPIOB,&lightr); //以上三个都要传入该函数初始化
}

uint8_t get_LightR_DO(void)//用来获取光敏电阻输出的数字量DO值
{
	return  GPIO_ReadInputDataBit(GPIOB,lightr.GPIO_Pin);
}

/***************************************************/LED模块
#include "LED.h"
#include "stm32f10x.h"                  // Device header

GPIO_InitTypeDef led;  //定义两个IO的结构体变量 A是PA0口、B是PB0口

void LED_init(uint16_t GPIO_Pin)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );//使能GPIOA的时钟
	
	
	led .GPIO_Mode=GPIO_Mode_Out_PP;//设置为推挽输出模式
	led .GPIO_Pin=GPIO_Pin;  //设置为所有Pin口
	led .GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz
	GPIO_Init( GPIOA,&led ); //以上三个都要传入该函数初始化
	
}

void LED_state(unsigned char Flag)//指定Pin口和对应状态
{
	if(Flag==0)
		GPIO_ResetBits(GPIOA,led.GPIO_Pin);//置低电平
	else
		GPIO_SetBits(GPIOA,led.GPIO_Pin);//置高电平
}

/***************************************************/主函数

int main(void)
{
	uint16_t lightstate;//亮、暗状态
	
	LED_init(GPIO_Pin_0);
	Buzzer_init(GPIO_Pin_0);
	LightR_init(GPIO_Pin_1);
	while(1)
	{
		lightstate =get_LightR_DO();//循环里实时获取
		if(lightstate)//暗	灯灭、蜂鸣器响
		{
			LED_state(1);
			Buzzer_run(0);
		}
		else //亮	灯亮、蜂鸣器不响
		{
			LED_state(0);
			Buzzer_run(1);
		}
	}
}

/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

四、OLED显示屏

1.结构原理

1.1 结构图(0.96寸4针B版本)

1.2 介绍

1.3 电路

1.4 调试方法

2.OLED驱动函数

3.驱动函数封装包

见资源包

五、EXTI外部中断系统

1.结构原理

1.1 认识中断

和C51中的中断是一样的,只不过32的资源更加丰富;  

1.2 中断系统内部机制(中断和异常向

         由于Stm32中断资源众多,所以这里的中断都是由NVIC统一管理。

左边需要执行的众多中断会根据分组和优先级排队,其中抢占优先级具有最高优先级可直接由NVIC传入CPU,中断CPU正在执行的主程序或者中断),而响应优先级则具有插队排第一位的权限优先级次于抢占优先级),当左边排队完成,NVIC会根据左边的排队决定当前该执行哪一个中断然后传给CPU(CPU不知道优先级逻辑,由NVIC处理,节约算力和IO口

 中断和异常向表,建议翻看手册;

1.3 EXTI简介

触发中断,中断响应是正常的流程,但是Stm32还提供了一种额外的中断处理:事件响应

当选择触发事件响应,那么该中断信号将不会通过NVIC传入CPU,而是直接传给某一个外设并触发它。例如触发ADC模块‘触发DMA等。

总结:中断响应是正常的流程,引脚电平变化触发中断。事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合操作。

注意:相同的pin口的中断不能同时被触发,如PA0、PB0、PC0三个中断同时需要传入NVIC,但是最终只会有一个被传入。

在EXTI内部有对数据筛选处理的模块,最终只有指定的IO数据会传入NVIC,其它的会作为事件触发传给指定的外设。

1.4 EXTI机制

1.5 AFIO复用IO口

2.点触式旋转编码器

2.1 结构原理

 2.2 代码实现(详细步骤说明)

该模块需要用到中断,如何实现呢?其实原理就是将模块IO口使能使其数据能够到达NVIC再到CPU就好了。

五个步骤:

1.打开对应IO口的RCC外设时钟(不打开IO口不能工作);

2.配置IGPO口的模式;

3.配置AFIO,使其选择我们要用的GPIO口,连接到EXTI;

4.配置EXTI模式,选择触发方式及响应方式(中断or事件);

5.配置NVIC,选择一个合适的中断优先级。

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "mokuaicount.h"
extern int num;

/***************************************************/初始化旋钮模块
#include "xuanniu.h"                  // Device header
#include "stm32f10x.h" 

void Xuanniu_init(uint16_t GPIO_Pin)//需要指定端口Pin进行模式设置
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能GPIOA的时钟
	
	GPIO_InitTypeDef Xuanniu_InitStruct;  

	Xuanniu_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//设置为 上拉输入 模式
	Xuanniu_InitStruct.GPIO_Pin=GPIO_Pin;  //外界参数设置Pin口
	Xuanniu_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz,在输入模式下该设置没用
	GPIO_Init( GPIOA,&Xuanniu_InitStruct); //以上三个都要传入该函数初始化
}

/***************************************************/配置中断
//**该模块由中断函数实现,可接入任意输入模式的模块实现计数
//**这里引入Key实现数据的减法,LightR实现加法
#include "stm32f10x.h"                  // Device header
#include "mokuaicount.h"
#include "Delay.h"
#include "xuanniu.h"


int num;

void Count_interrupt_init(void)
{
	//1、2、**步骤一二在模块内已经实现
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//AFIO时钟需要使能
	
	Xuanniu_init(GPIO_Pin_0|GPIO_Pin_1);
	//3**配置AFIO,选择输入引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//Xuanniu旋钮
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
	//4**配置EXTI寄存器,选择触发方式,这里配置EXTI和GPIO一样需要定义一个结构体
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line=EXTI_Line0|EXTI_Line1;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStruct);
	//5**配置NVIC,选择合适的中断优先级 --------每一个外设要单独配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//要先分组,整个系统NVIC只分配一种模式
	//**配置Xuanniu A端
	NVIC_InitTypeDef NVIC_InitStruct_Xuanniu;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannel=EXTI0_IRQn;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct_Xuanniu);
	//**配置Xuanniu C端
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannel=EXTI1_IRQn;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct_Xuanniu);
	
	
	//****所有配置完成
}

//中断位置随便放,也不需要声明
void EXTI0_IRQHandler()//正转触发
{
	if(EXTI_GetITStatus(EXTI_Line0)==SET)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0)
		{
			if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==1)
			{
				num++;
			}
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI1_IRQHandler()
{
	if(EXTI_GetITStatus(EXTI_Line1)==SET)
	{
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==0)
		{
			if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
			{
				num--;
			}
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line1);
}


/***************************************************/主函数
int main(void)
{
	OLED_Init();
	Count_interrupt_init();
	OLED_ShowString(1,1,"Hello,my honey");
	OLED_ShowString(2,1,"Hello WangFang");
	OLED_ShowString(3,1,"Hello Tomorrow");
	while(1)
	{
		OLED_ShowSignedNum(4,1,num,5);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

六、TIM定时中断

1.结构原理

1.1 介绍

1.2 定时器种类

不同型号的开发板拥有的定时器资源不一样

基本定时器只支持向上计数(由0加到重装载值触发中断后清零)

通用定时器和高级定时器都支持向上计数、向下计数、中央对齐三种模式计数

1.3 基本定时器框图

1.4 通用定时器框图

1.5 高级定时器框图

1.6 定时中断基本结构

1.7 时序图(详细解释)

计数流程:选择时钟源-->确定滤波的几分频-->确定预分频PSC-->确定重装载值ARR-->溢出触发中断

分频过程:外部输入规律的高低电平脉冲信号,经过选择分频模式后,信号转化输入预分频器开始计数,每到达预分频器设置的阈值溢出时就产生一个脉冲给计数器,计数器+1,最后当计数器计数值等于自动装载器设置的阈值时产生溢出,触发中断,因此在一分频模式下:

定时频率(时间)=(预分频值/初始时钟频率)*预装载值

如定时1s,则1 s=(7200)/72Mhz * 10000                     (取值范围 0~65535)

这就像是两个定时器级联一样,增大了计数范围

最新理解:

已知公式 t=1/f

那么内部时钟经过预分频后每记一次的时间就为:t1=1/(72Mhz/PSC+1)

那么分频器每隔t1时间就产生信号给计数器,直到计数器经过t2=t1*(ARR+1)的时间或者说累加到等于ARR时,产生中断同时计数器清零。

所以整个过程花费时间(每次中断的间隔时间)为:t2=1/(72Mhz/PSC+1)*(ARR+1)

比如要计时 1s :1 s = 1 / ( 72Mhz / 7200)* 10000

2.定时器中断计数(代码)

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "timer2.h"

/***************************************************/定时器2
#include "stm32f10x.h"
#include "timer2.h"

extern int num;
void timer2_init(void)
{
	//**1.打开总的RCC
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	//**2.选定定时器接入的时钟
	TIM_InternalClockConfig(TIM2);//设置定时器2使用内部时钟,可不写,上电默认
	//**3.配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=10000-1;//设置预装载值
	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除
	//**4.使能时基单元的中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	//**5.配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组
	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
	//**6.开始计数
	TIM_Cmd(TIM2,ENABLE);

}

//**配置中断函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		num++;
	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//一定要清除标志位
}

/***************************************************/主函数

int num;
int main(void)
{
	OLED_Init();
	timer2_init();
	OLED_ShowString(1,1,"Hello,my honey");
//	OLED_ShowString(2,1,"Hello WangFang");
//	OLED_ShowString(3,1,"Hello Tomorrow");
//	OLED_ShowString(4,1,"I miss you");
	while(1)
	{
		OLED_ShowString(2,1,"NUM:");
		OLED_ShowNum(2,5,num,5);
		
		OLED_ShowString(3,1,"Prescaler:");
		OLED_ShowNum(3,11,TIM_GetPrescaler(TIM2),5);
		
		OLED_ShowString(4,1,"Counter:");
		OLED_ShowNum(4,9,TIM_GetCounter(TIM2),5);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

3.外部时钟的定时器中断(代码)

相对于2的使用内部时钟,此方法不同之处仅在于使用函数:

TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//第二个设置分频,第三个设置触发方式,第四个设置采样频率(滤波器)

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "timer2.h"

/***************************************************/定时器2
#include "stm32f10x.h"
#include "timer2.h"

extern int num;
void timer2_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//**2.配置定时器接入外部时钟 同时配置外部时钟输入的引脚
	//TIM_InternalClockConfig(TIM2);//设置定时器2使用内部时钟,可不写,上电默认
	
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//第二个设置分频,第三个设置触发方式,第四个设置采样频率(滤波器)
	//**3.配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
//	TIM_TimeBaseInitStruct.TIM_Period=10000-1;//设置预装载值       //外部时钟的话用不了这么高的频率
//	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_Period=10-1;//设置预装载值
	TIM_TimeBaseInitStruct.TIM_Prescaler=2-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除
	//**4.使能时基单元的中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	//**5.配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组
	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
	//**6.开始计数
	TIM_Cmd(TIM2,ENABLE);

}

//**配置中断函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		num++;
	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

}


/***************************************************/主函数

int num;
int main(void)
{
	OLED_Init();
	timer2_init();
	OLED_ShowString(1,1,"Hello,my honey");
//	OLED_ShowString(2,1,"Hello WangFang");
//	OLED_ShowString(3,1,"Hello Tomorrow");
//	OLED_ShowString(4,1,"I miss you");
	while(1)
	{
		OLED_ShowString(2,1,"NUM:");
		OLED_ShowNum(2,5,num,5);
		
		OLED_ShowString(3,1,"Prescaler:");
		OLED_ShowNum(3,11,TIM_GetPrescaler(TIM2),5);
		
		OLED_ShowString(4,1,"Counter:");
		OLED_ShowNum(4,9,TIM_GetCounter(TIM2),5);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

4.TIM(OC)比较输出(PWM)!!!

4.1 结构原理介绍(模式)

 8种模式

 TIM_OCMode                        函数库描述                             解释


TIM_OCMode_Timing       TIM输出比较                      冻结,输出比较不起作用
TIM_OOCMode_Active     TIM输出比较主动模式        当比较发生时,强制输出高电平
TIM_OCMode_Inactiive    TIM输出比较非主动模式     当比较发生时,强制输出低电平
TIM_OCMode_Toggle       TIM输出比较触发模式        当比较发生时,输出翻转
TIM_OCMode_PWM1        TIM脉冲宽度调制模式1      PWM1
TIM_OCMode_PWM2        TIM脉冲宽度调制模式2      PWM2

4.2 输出设备(舵机,直流电机)

4.3 代码配置

4.3.1(呼吸灯、重映射)

先和配置定时器步骤差不多,只不过最后不需要配置中断和NVIC,就直接配置CCR

CCR需要先使用TIM_OCStructInit()整体初始化,然后再单独配置需要的模式:

    TIM_OCInitTypeDef TIM_OCInitStruct;
    TIM_OCStructInit(&TIM_OCInitStruct);//先整体初始化,防止有些变量未初始化而出错
    TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//配置为PWM1输出模式
    TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//配置REF为高极性
    TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//配置输出使能
    TIM_OCInitStruct.TIM_Pulse=100;//配置CCR计数器值
    TIM_OC1Init(TIM2,&TIM_OCInitStruct);

重映射使用看视频学习

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "PWM.h"
/***************************************************/PWM模块
#include "stm32f10x.h"                  // Device header
#include "Delay.h"


void pwm_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**3.配置时基单元
	//配置为1000KHz,分辨率为1%
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=100-1;//设置预装载值       //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除

	//**4.配置CCR
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//先整体初始化,防止有些变量未初始化而出错
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//配置为PWM1输出模式
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//配置REF为高极性
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//配置输出使能
	TIM_OCInitStruct.TIM_Pulse=100;//配置CCR计数器值
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	//5.开始计数
	TIM_Cmd(TIM2,ENABLE);
}

void LED_run(void)
{
	uint16_t num;
	for(num=100;num>0;num--)
	{
		TIM_SetCompare1(TIM2,num);
		Delay_ms(10);

	}
	for(num=0;num<100;num++)
	{
		TIM_SetCompare1(TIM2,num);
		Delay_ms(10);
	}
}

/***************************************************/主函数

int main(void)
{
	OLED_Init();
	pwm_init();
	OLED_ShowString(1,1,"Hello,my honey");
//	OLED_ShowString(2,1,"Hello WangFang");
//	OLED_ShowString(3,1,"Hello Tomorrow");
//	OLED_ShowString(4,1,"I miss you");
	while(1)
	{
		LED_run();
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
4.3.2(旋钮控制舵机角度PWM)

旋转旋钮触发中断从而改变CCR的值(改变占空比)

要注意舵机的信号周期为20ms,既50Hz

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "PWM.h"
#include "mokuaicount.h"

/***************************************************/PWM模块
#include "stm32f10x.h"                  // Device header
#include "PWM.h"
#include "Delay.h"


void pwm_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚PA0
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**3.配置时基单元
	//配置为1000KHz,分辨率为1%
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=20000-1;//设置预装载值       //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除

	//**4.配置CCR
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0;
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	//5.
	//**6.开始计数
	TIM_Cmd(TIM2,ENABLE);
}

void set_angle(uint16_t angle)
{
	TIM_SetCompare1(TIM2,angle);
}

/***************************************************/GPIO初始化
#include "xuanniu.h"                
#include "stm32f10x.h" 

void Xuanniu_init(uint16_t GPIO_Pin)//需要指定端口Pin进行模式设置
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE);//使能GPIOA的时钟
	
	GPIO_InitTypeDef Xuanniu_InitStruct;  

	Xuanniu_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//设置为 上拉输入 模式
	Xuanniu_InitStruct.GPIO_Pin=GPIO_Pin;  //外界参数设置Pin口
	Xuanniu_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;  //输出速度为50MHz,在输入模式下该设置没用
	GPIO_Init( GPIOB,&Xuanniu_InitStruct); //以上三个都要传入该函数初始化
}

/***************************************************/计数模块
//**该模块由中断函数实现,可接入任意输入模式的模块实现计数
//**这里引入Key实现数据的减法,LightR实现加法
#include "stm32f10x.h"                  // Device header
#include "mokuaicount.h"
#include "Delay.h"
#include "xuanniu.h"


extern int num;

void Count_interrupt_init(void)
{
	//1、2、**步骤一二在模块内已经实现
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//AFIO时钟需要使能
	
	Xuanniu_init(GPIO_Pin_0|GPIO_Pin_1);
	//3**配置AFIO,选择输入引脚PB1(C),PB0(A)
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//Xuanniu旋钮
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
	//4**配置EXTI寄存器,选择触发方式,这里配置EXTI和GPIO一样需要定义一个结构体
	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line=EXTI_Line0|EXTI_Line1;
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStruct);
	//5**配置NVIC,选择合适的中断优先级 --------每一个外设要单独配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//要先分组,整个系统NVIC只分配一种模式
	//**配置Xuanniu A端
	NVIC_InitTypeDef NVIC_InitStruct_Xuanniu;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannel=EXTI0_IRQn;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct_Xuanniu);
	//**配置Xuanniu C端
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannel=EXTI1_IRQn;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct_Xuanniu.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct_Xuanniu);
	
	
	//****所有配置完成
}

//中断位置随便放,也不需要声明
void EXTI0_IRQHandler()//正转触发
{
	if(EXTI_GetITStatus(EXTI_Line0)==SET)
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
		{
			if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==1)
			{
				num+=100;
			}
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI1_IRQHandler()
{
	if(EXTI_GetITStatus(EXTI_Line1)==SET)
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
		{
			if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==1)
			{
				num-=100;
			}
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line1);
}


/***************************************************/主函数
int num=1500;

int main(void)
{
	OLED_Init();
	pwm_init();
	Count_interrupt_init();
	OLED_ShowString(1,1,"Hello,my baby");
	set_angle(num);//默认居中
	while(1)
	{	
		if(num<=0)
		{
			num=0;
			OLED_ShowString(2,8,"Left Max ");
		}
		else if(num<1500)
		{
			OLED_ShowString(2,8,"Left     ");
		}
		else if(num==1500)
		{
			OLED_ShowString(2,8,"Midel    ");
		}
		else if(num<2500)
		{
			OLED_ShowString(2,8,"Right    ");
		}
		else if(num>=2500)
		{
			num=2500;
			OLED_ShowString(2,8,"Right Max");
		}
		OLED_ShowNum(2,1,num,5);
		
		set_angle(num);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
4.3.3 直流电机(调速)

stay待机控制直接接入高电平3.3v就可以了

in1、in2初始化GPIO(推完输出模式)然后设置高低电平即可

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Motor.h"

/***************************************************/PWM模块
#include "stm32f10x.h"                  // Device header
#include "PWM.h"
#include "Delay.h"


void pwm_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚PA0
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**3.配置时基单元
	//配置为1000KHz,分辨率为1%
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=100-1;//设置预装载值       //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除

	//**4.配置CCR
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0;
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	//5.开始计数
	TIM_Cmd(TIM2,ENABLE);
}

void set_num(uint16_t num)
{
	TIM_SetCompare1(TIM2,num);
}

/***************************************************/电机模块
#include "stm32f10x.h"                  // Device header
#include "Motor.h"
#include "PWM.h"

void Motor_init(void)
{
	pwm_init();
	
	GPIO_InitTypeDef GPIO_InitStruct;//初始化两个方向控制引脚:PA4,PA5
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_Init(GPIOA,&GPIO_InitStruct);
}

void set_speed(int speed)
{
	if(speed>=0)
	{
		set_num(speed);
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
	}
	else
	{
		set_num(-speed);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
	}
}

/***************************************************/主函数


int main(void)
{
	OLED_Init();
	Motor_init();
	while(1)
	{	
		set_speed(-100);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

5.TIM输入捕获

5.1 结构原理

利用定时器模块计数,当输入捕获模块的边沿检测模块检测到下降沿或上升沿,触发获取CNT的值,每记完一次后CNT清零,获取到的该周期的CNT值再通过与CNT计数频率即可得到该电平变化周期的时间T。

由此可见,测频法适合测量高频,测周法适合测量低频 

 

5.2 代码配置

5.2.1 单通道测频率

这里采用TIM2产生PWM信号用于测量

配置TIM3时,也需要配置定时器时基模块使CNT计数,然后配置IC模块(通过一个结构体),再配置从模式的触发源和触发事件,使能定时器即可。

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

/***************************************************/PWM模块
#include "stm32f10x.h"                  // Device header


void pwm_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**3.配置时基单元
	//配置为1000KHz,分辨率为1%
	TIM_InternalClockConfig(TIM2);//选择内部时钟源
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=100-1;//设置预装载值       //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除

	//**4.配置CCR
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	TIM_OCStructInit(&TIM_OCInitStruct);//先整体初始化,防止有些变量未初始化而出错
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//配置为PWM1输出模式
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//配置REF为高极性
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//配置输出使能
	TIM_OCInitStruct.TIM_Pulse=0;//配置初始CCR计数器值
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	//**5.开始计数
	TIM_Cmd(TIM2,ENABLE);
}

void set_Zhankongbi(uint16_t  num)
{
	TIM_SetCompare1(TIM2,num);//通过改变CCR的值,从而改变CCR/CNT占空比
}

void set_PSC(uint16_t  num)
{
	TIM_PrescalerConfig(TIM2,num,TIM_PSCReloadMode_Immediate);//改变PSC的值
}	

/***************************************************/IC输入捕获模块
#include "stm32f10x.h"                  // Device header
#include "IC.h"

void IC_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//打开TMI3,因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚PA6
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**选择内部时钟源
	TIM_InternalClockConfig(TIM3);
	//**3.配置时基单元
	//配置为1MHz
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=65536-1;//设置预装载值   ARR    //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;//设置预分频值   PSC
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);//初始化时会产生标志位,需要清除
	
	//**4.配置IC模块
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;//选择通道一
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//选择上升沿触发
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;//选择正极性
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//选择一分频
	TIM_ICInitStruct.TIM_ICFilter=0xF;//选择滤波器的值
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	
	//**5.配置从模式
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
	
	//**6.使能定时器模块,开始计数
	TIM_Cmd(TIM3,ENABLE);
}

uint32_t get_Freq(void)
{
	return 1000000/(TIM_GetCapture1(TIM3)+1) ;
}


/***************************************************/主函数
int main(void)
{
	OLED_Init();
	pwm_init();
	IC_init();
	OLED_ShowString(1,1,"Hello,my honey");
	set_Zhankongbi(50);			//Freq = 72M / (PSC + 1) / 100
	set_PSC(720 - 1);			//Duty = CCR / 100
	while(1)
	{
		OLED_ShowNum(2,1,get_Freq(),5);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
5.2.2 PWMI模式同时测频率、占空比

相当于开了TI1FP2的通道去实现读取占空比

占空比是 CCR2记录的高电平持续时间 除以 CCR1记录的整个周期的时间

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

/***************************************************/PWM模块
#include "stm32f10x.h"                  // Device header


void pwm_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**3.配置时基单元
	//配置为1000KHz,分辨率为1%
	TIM_InternalClockConfig(TIM2);//选择内部时钟源
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=100-1;//设置预装载值       //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除

	//**4.配置CCR
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	TIM_OCStructInit(&TIM_OCInitStruct);//先整体初始化,防止有些变量未初始化而出错
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;//配置为PWM1输出模式
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;//配置REF为高极性
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//配置输出使能
	TIM_OCInitStruct.TIM_Pulse=0;//配置初始CCR计数器值
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	//**5.开始计数
	TIM_Cmd(TIM2,ENABLE);
}

void set_Zhankongbi(uint16_t  num)
{
	TIM_SetCompare1(TIM2,num);//通过改变CCR的值,从而改变CCR/CNT占空比
}

void set_PSC(uint16_t  num)
{
	TIM_PrescalerConfig(TIM2,num,TIM_PSCReloadMode_Immediate);//改变PSC的值
}	

/***************************************************/IC输入捕获模块
#include "stm32f10x.h"                  // Device header
#include "IC.h"

void IC_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//打开TMI3,因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//**2.配置PWM输出引脚PA6
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//**选择内部时钟源
	TIM_InternalClockConfig(TIM3);
	//**3.配置时基单元
	//配置为1MHz
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数
	TIM_TimeBaseInitStruct.TIM_Period=65536-1;//设置预装载值   ARR    //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;//设置预分频值   PSC
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);//初始化时会产生标志位,需要清除
	
	//**4.配置IC模块
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;//选择通道一
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//选择上升沿触发
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;//选择正极性
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//选择一分频
	TIM_ICInitStruct.TIM_ICFilter=0xF;//选择滤波器的值
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	TIM_PWMIConfig(TIM3,&TIM_ICInitStruct);

	//**5.配置从模式
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
	
	//**6.使能定时器模块,开始计数
	TIM_Cmd(TIM3,ENABLE);
}

uint32_t get_Freq(void)
{
	return 1000000/(TIM_GetCapture1(TIM3)+1) ;
}

uint32_t get_Zhankongbi(void)
{
	return (TIM_GetCapture2(TIM3)+1)*100/(TIM_GetCapture1(TIM3)+1);
}
/***************************************************/主函数
int main(void)
{
	OLED_Init();
	pwm_init();
	IC_init();
	OLED_ShowString(1,1,"Hello,my honey");
	set_Zhankongbi(50);			//Freq = 72M / (PSC + 1) / 100
	set_PSC(720 - 1);			//Duty = CCR / 100
	while(1)
	{
		OLED_ShowNum(2,1,get_Freq(),5);
        OLED_ShowNum(3,1,get_Zhankongbi(),3);
	}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
5.2.3 编码器接口测速(简介)

通过配置定时器,占用了IC捕获模式的两个通道,自动判断两个脚的波形相位差和边沿触发(一般都是使用T1T2模式,可消除毛刺信号

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Bianmaqi.h"
#include "Delay.h"
#include "timer2.h"

/***************************************************/延时函数
#include "Delay.h"
#include "stm32f10x.h" 

/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

/**
  * @brief  毫秒级延时
  * @param  xms 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
 
/**
  * @brief  秒级延时
  * @param  xs 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 

/***************************************************/编码器
#include "stm32f10x.h"                  // Device header
#include "Bianmaqi.h"

void Bianmaqi_init(void)
{
	//1**使能时钟
	RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE);
	
	//2**定义GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6 |GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//3**初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_Channel_1|TIM_Channel_2;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_Period=65536-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler=1-1;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	//4**初始化IC模块(部分)两个Pin口
	TIM_ICInitTypeDef TIM_ICInitStruct;
	TIM_ICStructInit(&TIM_ICInitStruct);
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
	TIM_ICInitStruct.TIM_ICFilter=0xF;
//	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
//	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;
//	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;
	TIM_ICInitStruct.TIM_ICFilter=0xF;
//	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
//	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;
//	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM3,&TIM_ICInitStruct);
	
	//5**配置编码器
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Falling,TIM_ICPolarity_Rising);//控制极性
	
	//6.使能定时器TIM3
	TIM_Cmd(TIM3,ENABLE);
}

int16_t get_cnt(void)
{
	int16_t temp=0;
	temp=TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3,0);
	return temp;
}

/***************************************************/定时器TIM2
#include "stm32f10x.h"
#include "timer2.h"

extern int num;
void timer2_init(void)
{
	//**1.打开总的RCC,要配置GPIO从而传入外部时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//因为定时器2是通用定时器,在APB1总线上
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//**2.配置定时器接入外部时钟 同时配置外部时钟输入的引脚
	TIM_InternalClockConfig(TIM2);//设置定时器2使用内部时钟,可不写,上电默认
	
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	//TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//第二个设置分频,第三个设置触发方式,第四个设置采样频率(滤波器)
	//**3.配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置向上l计数
	TIM_TimeBaseInitStruct.TIM_Period=10000-1;//设置预装载值       //外部时钟的话用不了这么高的频率
	TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;//设置预分频值
//	TIM_TimeBaseInitStruct.TIM_Period=10-1;//设置预装载值
//	TIM_TimeBaseInitStruct.TIM_Prescaler=2-1;//设置预分频值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;//高级计数器功能,重复计数器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);//初始化时基单元
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//初始化时会产生标志位,需要清除
	//**4.使能时基单元的中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	//**5.配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组
	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
	//**6.开始计数
	TIM_Cmd(TIM2,ENABLE);

}
/*
//配置中断函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		num++;
	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

}
*/

/***************************************************/主函数
int16_t speed=0;
int main(void)
{
	timer2_init();
	OLED_Init();
	Bianmaqi_init();
	OLED_ShowString(1,1,"Hello,my honey");
	while(1)
	{
		OLED_ShowSignedNum(2,1,speed,6);
	}
} 

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
	{
		speed=get_cnt();
		Delay_ms (10);
	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

七、ADC模数转换

1.结构原理

1.1 介绍

1.2 原理

采样、抽取、量化、编码

1.3 ADC结构框图

1.4 stm32片上资源

1.5 ADC工作的4种模式

1.5.1 单次转换,非扫描模式

1.5.2 连续转换,非扫描模式

1.5.3 单次转换,扫描模式

1.5.4 连续转换,扫描模式 

1.6 ADC的触发方式(软件,硬件)

1.7 ADC的数据处理、转换时间、校准

2.代码配置

2.1 ADC单通道模式

单次转换、非连续模式

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "ADC.h"
#include "Delay.h"

/***************************************************/ADC模块
#include "stm32f10x.h"                  // Device header
#include "ADC.h"


void ADC_init(void)
{
	//1.先使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//2.设置ADC的分频
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	//3.配置GPIO口PA0
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//4.确定规则组或注入组的通道
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_28Cycles5);
	
	//5.初始化ADC
	ADC_InitTypeDef ADC_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;//配置是否连续触发模式
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;//选择数据对齐模式为右对齐
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//选择外界触发源
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;//配置是ADC是几个同时工作还是独立工作
	ADC_InitStruct.ADC_NbrOfChannel=1;//确定要输入的组的个数
	ADC_InitStruct.ADC_ScanConvMode=DISABLE;//是否连续扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);
	
	//6.打开ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//7.校准ADC
	ADC_ResetCalibration(ADC1);//1.复位校准
	while(ADC_GetResetCalibrationStatus(ADC1));//2.等待复位校准完成
	ADC_StartCalibration(ADC1);//3.开始校准
	while(ADC_GetCalibrationStatus(ADC1));//4.等待校准完成
}

uint16_t get_adcvalue(void)
{
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//触发开始ADC读值
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);//读取EOC标志位,当转化读取完成后退出循环
	return ADC_GetConversionValue(ADC1);//读取ADC寄存器,自动清除EOC标志位
}

/***************************************************/延时函数
#include "Delay.h"
#include "stm32f10x.h" 

/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

/**
  * @brief  毫秒级延时
  * @param  xms 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
 
/**
  * @brief  秒级延时
  * @param  xs 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 

/***************************************************/主函数

uint16_t ADCvalue;//旋钮值
float  Voltage;//转换成电压值
int main(void)
{
	OLED_Init();
	ADC_init();
	OLED_ShowString(1,1,"Hello,my honey");

	OLED_ShowString(2, 1, "ADValue:");
	OLED_ShowString(3, 1, "Volatge:0.00V");
	while(1)
	{
		ADCvalue=get_adcvalue();
		Voltage = (float)ADCvalue / 4095 * 3.3;
		
		OLED_ShowNum(2, 9, ADCvalue, 4);
		OLED_ShowNum(3, 9, Voltage, 1);//这里将电压值按整数部分和小数部分两部分取出来
		OLED_ShowNum(3, 11, (uint16_t)(Voltage * 100) % 100, 2);
		
		Delay_ms(100);;
	}
}


/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

**2.简单实现ADC多通道

每次改变序号里的通道即可;

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "ADC.h"
#include "Delay.h"

/***************************************************/ADC
#include "stm32f10x.h"                  // Device header
#include "ADC.h"


void ADC_init(void)
{
	//1.先使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//2.设置ADC的分频
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	//3.配置GPIO口PA0
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//4.确定规则组或注入组的通道
//	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_28Cycles5);
	
	//5.初始化ADC
	ADC_InitTypeDef ADC_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;//配置是否连续触发模式
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;//选择数据对齐模式为右对齐
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//选择外界触发源
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;//配置是ADC是几个同时工作还是独立工作
	ADC_InitStruct.ADC_NbrOfChannel=1;//确定要输入的组的个数
	ADC_InitStruct.ADC_ScanConvMode=DISABLE;//是否连续扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);
	
	//6.打开ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//7.校准ADC
	ADC_ResetCalibration(ADC1);//1.复位校准
	while(ADC_GetResetCalibrationStatus(ADC1));//2.等待复位校准完成
	ADC_StartCalibration(ADC1);//3.开始校准
	while(ADC_GetCalibrationStatus(ADC1));//4.等待校准完成
}

uint16_t get_adcvalue(uint8_t ADC_Channel)
{
		ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_28Cycles5);

	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//触发开始ADC读值
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);//读取EOC标志位,当转化读取完成后退出循环
	return ADC_GetConversionValue(ADC1);//读取ADC寄存器,自动清除EOC标志位
}
     
/***************************************************/延时
#include "Delay.h"
#include "stm32f10x.h" 

/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

/**
  * @brief  毫秒级延时
  * @param  xms 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
 
/**
  * @brief  秒级延时
  * @param  xs 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 

/***************************************************/主函数
uint16_t key,light;

int main(void)
{
	OLED_Init();
	ADC_init();
	OLED_ShowString(1,1,"Hello,my honey");
//	OLED_ShowString(2,1,"Hello WangFang");
//	OLED_ShowString(3,1,"Hello Tomorrow");
//	OLED_ShowString(4,1,"I miss you");

	while(1)
	{
		key=get_adcvalue(ADC_Channel_0);
		light=get_adcvalue(ADC_Channel_1);
		
		OLED_ShowNum(2,1,key,5);
		OLED_ShowNum(3,1,light,5);
	}
}

/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/

3.DMA实现数据迁移

**1.结构原理

待续

8.USART串口

1.

  • 5
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
gd32f103c8t6stm32f103c8t6是两种不同的芯片型号,分别由两家不同的公司生产。 首先,gd32f103c8t6是全志科技推出的一款32位低功耗微控制器芯片,其基于ARM Cortex-M3内核。它在性能、功耗和功能方面与stm32f103c8t6非常相似。gd32f103c8t6提供了高达72 MHz的时钟频率,内置了一系列的外设,例如通用串行总线(USART)、通用同步异步接收传输器(SPI)和通用定时器(TIM),并且具有较低的功耗以满足嵌入式应用的需求。另外,gd32f103c8t6提供了更丰富的Flash存储容量和SRAM容量可供用户使用,使其具备更好的性价比。 而stm32f103c8t6则是意法半导体(STMicroelectronics)公司的一款32位低功耗微控制器芯片,同样基于ARM Cortex-M3内核。它在市场上有较好的知名度,并被广泛应用于各个领域的嵌入式系统。stm32f103c8t6也提供了高达72 MHz的时钟频率,内置了丰富的外设,如UART、SPI和定时器等,以及较低的功耗。它也具备较大的Flash和SRAM容量。 总体而言,gd32f103c8t6stm32f103c8t6在技术指标上非常接近,功能和性能基本一致。它们的区别主要在于生产厂商和市场认可度。由于全志科技是中国的公司,所以gd32f103c8t6在中国市场有较高的知名度和应用价值,而stm32f103c8t6则因为STMicroelectronics是国际知名公司,在全球范围内更为广泛地被采用。不同芯片所使用的开发工具和支持也会有所不同,需要根据实际需求选择合适的芯片型号。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值