STM32学习笔记

STM32学习笔记

一、STM32简介

1.、STM32片上资源/外设

英文缩写名称英文缩写名称
NVIC嵌套向量中断控制器CANCAN通讯
SysTICK系统滴答定时器USBUSB通讯
RCC复位和时钟控制RTC实时时钟
GPIO通用IO口CRCCRC校验
AFIO复用IO口PER电源控制
EXTI外部中断BKP备份寄存器
TIM定时器IWDG独立看门狗
ADC模数转换器WWDG窗口看门狗
DMA直接访问内存DAC数模转换器
USART同步/异步串口通讯SDIOSD卡接口
I2CI2C通讯FSMC可变静态存储控制器
SPISPI通讯USB OTGUSB主机接口

二、MDK新建工程

  • 建立工程文件夹,在keil中新建工程,选择型号

  • 工程文件中建立Start,Library,User等文件夹,复制固件库中的文件到工程文件夹

  • 工程里建立对应Start,Library,User等同名称的分组,然后将文件夹内的文件添加到工程分组

  • 依次点击魔法棒,C/C++,Include Paths内声明所有包含头文件的文件夹

  • 依次点击魔法棒,C/C++,Define内定义USE_STDPERIPH_DRIVER

  • 依次点击魔法棒,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run

  • 复位中断:该部分为程序的主入口,当STM32复位后,程序进入复位中断函数中执行,复位中断主要调用SystemInit以及main

三,GPIO

第一章:GPIO通用输出输入

第一节:GPIO模式
  • 通过配置GPIO的端口配置寄存器,端口可以配置以下8种模式
模式名称性质特征Code
浮空输入数字输入可读取引脚电平,若引脚悬空,则电平不稳定GPIO_Mode_IN_FLOATING
上拉输入数字输入可读取引脚电平,内部连接上拉电阻,悬空时默认高电平GPIO_Mode_IPU
下拉输入数字输入可读取引脚电平,内部连接下拉电阻,悬空时默认低电平GPIO_Mode_IPD
模拟输入模拟输入GPIO无效引脚直接进入内部ADCGPIO_Mode_AIN
开漏输出数字输出可引出引脚电平,高电平为高阻态,低电平接VSSGPIO_Mode_Out_OD
推挽输出数字输出可引出引脚电平,高电平接VDD,低电平接VSSGPIO_Mode_Out_PP
复用开漏输出数字输出由片上外设控制,高电平为高阻态。低电平接VSSGPIO_Mode_AF_OD
复用推挽输出数字输出由片上外设控制,高电平接VDD,低电平接VSSGPIO_Mode_AF_PP

知识点:

  1. 推挽输出高低电平都有驱动能力
  2. 开漏输出低电平有驱动能力,高电平则没有

项目一:点亮LED

#include "stm32f10x.h"                  // 引用STM32F10X头文件

int main(void)
{
    //打开APB2时钟,因为LED连接的是PC13要使用GPIO C,所以要打开APB2时钟
    //因为要使用CPIO C所以传入RCC_APB2Periph_GPIOC
    //ENABLE意为:打开,因为要打开时钟所以输入ENABLE,关闭则输入DISABLEE
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
    //定义GPIO结构体 名称为 GPIO_InitStructure
	GPIO_InitTypeDef GPIO_InitStructure;
    //对结构体进行引出
    //mode:配置GPIO的工作模式 GPIO_Mode_Out_PP:推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    //Pin:配置GPIO的引脚 因为使用PC13所以填入
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13;
    //Speed:配置GPIO的速度 选择50MHz
	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
    //配置GPIO因为使用GPIOC所以第一部分填入GPIOC
    //将结构体传入函数,参数是指针形式,所以添加取地址符“&”
	GPIO_Init(GPIOC,&GPIO_InitStructure);
    //让IO口输出高电平
	//GPIO_SetBits(GPIOC,GPIO_Pin_13);
    //让IO口输出低电平
    //为告知所以将GPIOC,GPIO_Pin_13传入
	GPIO_ResetBits(GPIOC,GPIO_Pin_13);
	while (1)
	{
	
	}
}

总结:

​ 1.在使用外设之前,要先打开对应的总线时钟

​ 2.初始化配置GPIO时,如果有不会使用的函数,可以跳转查看定义,也可以直接打开对应文件,去查看可以使用的函数

项目二:LED闪烁

#include  "stm32f10x.h"   
#include  "Delay.h"			//引用延时函数头文件

int main(void)
{
    //开启APB2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    //定义结构体变量
	GPIO_InitTypeDef GPIO_InitStructure;
    //配置GPIO为推挽输出模式,选择0引脚,频率设为50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    //对GPIO进行初始化配置
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	while (1){
    //点亮LED延时500MS后熄灭然后在点亮/熄灭继续循环
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
	Delay_ms(500);
	GPIO_SetBits(GPIOA,GPIO_Pin_0);
	Delay_ms(500);
	}
	
}

项目三:LED流水灯

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

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    // 初始化全部引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	while (1){
	GPIO_Write(GPIOA,~0X0001); // 0000 0000 0000 0001
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0002); // 0000 0000 0000 0010
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0004); // 0000 0000 0000 0100
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0008); // 0000 0000 0000 1000
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0010); // 0000 0000 0001 0000
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0020); // 0000 0000 0010 0000
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0040); // 0000 0000 0100 0000
	Delay_ms(500);
	GPIO_Write(GPIOA,~0X0080); // 0000 0000 1000 0000
	Delay_ms(500);
	}
	
}

项目四:蜂鸣器

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

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	
	while (1){
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	Delay_ms(500);
	GPIO_SetBits(GPIOB,GPIO_Pin_12);
	Delay_ms(500);
	}
	
}
  • 标注:因为使用有源蜂鸣器,所以只需通电便会发出叫声

项目五:按键控制LED

  • 在工程文件下新建“Hardware”用于存放硬件驱动程序
  • LED初始化
#include "stm32f10x.h"                  // Device header


//该函数为初始化LED
void LED_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 |GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);
}


//该函数为打开LED1
void LED1_NO(void){
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}

//该函数为关闭LED1
void LED1_OFF(void){
	GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
//在调用该函数时,LED1的状态取反
void LED1_Tum(void){
	//读取当前端口状态,如果当前状态为为1,则置0、如果为0,则值1;实现端口电平反转
	if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0)
	{
	GPIO_SetBits(GPIOA,GPIO_Pin_1);
	}
	else
	{
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);
	}
}

void LED2_NO(void){
	GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}

void LED2_OFF(void){
	GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
void LED2_Tum(void){
	//读取当前端口状态,如果当前状态为为1,则置0、如果为0,则值1;实现端口电平反转
	if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0)
	{
	GPIO_SetBits(GPIOA,GPIO_Pin_2);
	}
	else
	{
	GPIO_ResetBits(GPIOA,GPIO_Pin_2);
	}
}
  • 配置头文件
#ifndef __LED_H
#define __LED_H

void LED_Init(void);
void LED1_NO(void);
void LED1_OFF(void);
void LED1_Tum(void);
void LED2_NO(void);
void LED2_OFF(void);
void LED2_Tum(void);
	
#endif
  • 按键初始化
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

//以下为按键初始化函数
void Key_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef  GPIO_InitStructure;
	//因为此处需要读取按键所以选择上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStructure);
}

//以下为读取按键函数
uint8_t Key_GetNum(void){
	//按键初始化为0,如果没有按下按键返回0,
	uint8_t KeyNum = 0;
	//读取PB1端口值
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
		{
		//消抖
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
		}
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0)
		{
		//消抖
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
		}

	
	return KeyNum;
}
  • 按键初始化头文件
#ifndef __KEY_H
#define __KEY_H

void Key_Init(void);
uint8_t Key_GetNum(void);

#endif

  • main主函数
#include  "stm32f10x.h"   // Device header
#include  "Delay.h"
#include  "LED.h"
#include  "Key.h"

//存储按键返回值
uint8_t KeyNum;

int main(void)
{
	LED_Init();
	Key_Init();
	while(1)
	{
		//不断读取按键值赋值给KeyNum
	 KeyNum = Key_GetNum();
	//当按键一按下
		if(KeyNum == 1)
		{
				LED1_Tum();
		}
		if(KeyNum == 2)
		{
				LED2_Tum();
		}
	}
}

四、EXTI外部中断

第一章:EXTI简介

第一节:中断系统
  • EXTI外部中断
  • 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的的中断源
  • 中断嵌套:当一个中断程序正在运行时,又有新的更高优先的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后在依次返回
第二节:STM32中断

​ 1.68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设

​ 2.使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

第三节:NVIC优先级分组

​ 1.NVIC的中断优先级由优先寄存器的4位(0-15)决定,这四位可以进行切分,分为高n位的抢占优先级和低4-n位的抢占优先级

​ 2.抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

分组方式抢占优先级响应优先级
分组00位,取值为04位,取值为0-15
分组11位,取值0-13位,取值为0-7
分组22位,取值为0-32位,取值为0-1
分组33位,取值为0-71位,取值为0-1
分组44位,取值为0-150位,取值为0
第四节:EXTI简介
  • EXTI:外部中断
  • EXTI可以检测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI立即向NVIC发送中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
  • 支持的触发方式:上升沿/下降沿/双边沿/软件触发
  • 支持的GPIO口:所有的GPIO口,但相同的pin不能同时触发中断
  • 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
  • 触发响应方式:中断响应/事件响应
第五节:配置EXTI中断步骤

​ 1.配置RCC,打开所相关的外设时钟

​ 2.配置GPIO,选择端口输入模式

​ 3.配置AFIO,选择使用的GPIO连接下级EXTI

​ 4.配置EXTI选择边沿触发模式。例如:上升沿,下降沿或双边沿

​ 5.配置NVIC选择合适的中断优先级

第六节:EXTI中断配置
项目一:对射式红外传感器计数
void CountSensor_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB14引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;					//选择配置外部中断的14号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;		//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}
项目二:旋转编码器计数
#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;					//全局变量,用于计数旋转编码器的增量值

/**
  * 函    数:旋转编码器初始化
  * 参    数:无
  * 返 回 值:无
  */
void Encoder_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB0和PB1引脚初始化为上拉输入
	
	/*AFIO选择中断引脚*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚
	
	/*EXTI初始化*/
	EXTI_InitTypeDef EXTI_InitStructure;						//定义结构体变量
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		//选择配置外部中断的0号线和1号线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//指定外部中断线使能
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//指定外部中断线为中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//指定外部中断线为下降沿触发
	EXTI_Init(&EXTI_InitStructure);								//将结构体变量交给EXTI_Init,配置EXTI外设
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			//选择配置NVIC的EXTI0线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设

	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			//选择配置NVIC的EXTI1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
}

/**
  * 函    数:旋转编码器获取增量值
  * 参    数:无
  * 返 回 值:自上此调用此函数后,旋转编码器的增量值
  */
int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/
	/*在这里,也可以直接返回Encoder_Count
	  但这样就不是获取增量值的操作方法了
	  也可以实现功能,只是思路不一样*/
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

/**
  * 函    数:EXTI0外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)		//判断是否是外部中断0号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)		//PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
			{
				Encoder_Count --;					//此方向定义为反转,计数变量自减
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line0);			//清除外部中断0号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

/**
  * 函    数:EXTI1外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)		//判断是否是外部中断1号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)		//PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
			{
				Encoder_Count ++;					//此方向定义为正转,计数变量自增
			}
		}
		EXTI_ClearITPendingBit(EXTI_Line1);			//清除外部中断1号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}



main主函数

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

int16_t NUM;

int main(void)
{
	Encoder_Init();
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	
	OLED_ShowString(1, 1, "NUM:");	//1行3列显示字符串HelloWorld!
	
	while (1)
	{
		NUM += Encoder_Get();
		OLED_ShowSignedNum(1,5,NUM,5);
	}
}

五、TIM定时器中断

第一章:TIM简介

  • TIM定时器
  • 定时器可以对输入的时钟计数,并在计数值达到设定值后触发中断
  • 16位计数器,预分频器,自动重装寄存器的时基单元,在72Mz计数时钟下可以实现最大59.65s的定时
  • 不仅具备基本定时中断功能,而且还包含内外时钟选择,输入捕获,输出比较,编码器接口,主从触发模式等多种功能
  • 根据应用场景分为:高级定时器,通用定时器,基础定时器三种类型

第二章:定时器类型

类型编号总线功能
高级定时器TIM1,TIM8APB2拥有通用定时器的所有功能,并额外具有重复计数器,死区生成、互补输出、刹车输入等功能
通用定时器TIM2,TIM3,TIM4,TIM5APB1拥有基本定时器全部功能,并额外具有内外部时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器TIM6,TIM7APB1拥有定时器中断,主模式触发DAC等功能
  • 在STM32F103C8T6定时器资源有:TIM1,TIM2,TIM3,TIM4(芯片不同拥有的定时器资源也不同)

第三章:定时中断的基本结构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第四章:TIM输出比较

1.TIM输出比较简介
  • OC输出比较
  • 输出比较可以通过比较CNT与CCR寄存器值的关系,来对比输出电平进行置1,置0,或翻转的操作,用于输出一定的频率和占空比的PWM波形
  • 每个高级定时器和通用定时器都拥有四个输出比较通道
  • 高级定时器的前三个通道额外拥有四区生成和互补输出的功能
2.PWM简介
  • PWM脉冲宽度调制
  • 在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效的获得所需要的模拟量,常用于电机控速等领域
  • PWM参数

频率 = 1 /Ts 占空比 = T on/Ts 分辨率 = 占空比变化步距

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.输出比较模式
模式描述
冻结CNT=CCR时,REF保持为原始状态
匹配时置有效电平CNT=CCR时,REF置有效电平
匹配时置无效电平CNT=CCR时,REF置无效电平
匹配时电平反转CNT=CCR时,REF电平反转
强制为无效模式CNT与CCR无效时,REF强制转为无效电平
强制为有效模式CNT与CCR无效时,REF强制转为有效电平
PWM模式1向上计数:CNT<CCR时,REF置有效电平,CNT>=CCR时,REF置无效电平 向下计数:CNT>CCR时,REF置无效电平,CNT<=CCR时,REF置有效电平
PWM模式2向上计数:CNT<CCR时,REF置无效电平,CNT>=CCR时,REF置有效电平 向下计数:CNT>CCR时,REF置有效电平,CNT<=CCR时,REF置无效电平
4.参数计算

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • PWM频率:Freq=CK_PSC/(PSC+1)/(ARR+1)
  • PWM占空比:Duty=CCR/(ARR+1)
  • PWM分辨率:Reso=1/(ARR+1)
5.舵机简介
  • 舵机是一种根据输入PWM信号占空比来控制输出角度的装置
  • 输入PWM信号要求:周期为20s,高电平宽带如为0.5-2.5s
输入信号脉冲宽度舵机输出轴转角
0.5ms-90
1ms-45
1.5ms-0
2ms45
2.590
6.直流电机及驱动简介
  • 直流电机是一种将电能转化为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
  • 直流电机属于大功率器件,GPIO无法直接驱动,需要配合电机驱动电机来进行操作
  • TB6612是一款双路H桥形直流电机驱动芯片,可以驱动两个直流电机并控制其转速及方向

项目一:定时器定时中断

六、ADC采集

一、ADC简介

  • ADC 模拟-数字转换器
  • ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
  • 12位逐次逼近型ADC,1us转换时间
  • 输入电压范围:0-3.3V,转换结果范围:0-4095
  • 18个输入通道,可测量16个外部和2个内部信号源
  • 规则组和注入组两个转换的单元
  • 模拟看门狗自动检测输入电压范围
  • STM32F103C8T6 ADC资源:ADC1,ADC2,10个外部通道

二、转换时间

  • AD转换的步骤:采样,保持,量化,编码
  • STM32 ADC的总转换时间为 Tconv=采样时间+12.5个ADC周期
  • 例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期

​ Tconv=1.5+12.5=14个ADC周期=1us

七、DMA

一、DMA简介

  • DMA 直接存储器存取
  • DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无需CPU干预,节省了CPU资源
  • 12个独立可配置通道:DMA1(7个通道),DMA2(5个通道)
  • 每个通道支持独立的软件触发和特定的硬件触发
  • STM32F10C8T6 DMA资源:DMA1(7个通道)

二、存储器映像

类型起始地址存储器用途
0X0800 0000程序存储器Flash存储C语言编译后的程序代码
ROM0X1FFF F000系统存储器存储BootLoader,用于串口下载
0X1FFF F800选项字节存储一些独立于程序代码的配置参数
0X2000 0000运行内存SRAM存储运行中的临时变量
RAM0X4000 0000外设寄存器存储各个外设的配置参数
0XE000 0000内核外设寄存器存储内核各个外设的配置参数

八、USART

一、通信接口

  • 通讯的目的:将一个设备的数据传输到另外一个设备,扩展硬件系统
  • 通讯协议:制定通讯的规则,通讯双方按照协议规则进行数据收发
名称引脚双工时钟电平设备
USARTTX,RX全双工异步单端点对点
I2CSCL,SDA半双工同步单端多设备
SPISCLK,MOSI,MISO.CS全双工同步单端多设备
CANCAN_H,CAN_L半双工异步差分多设备
USBDP,DM半双工异步差分点对点

二、硬件电路

  • 简单双向串口通讯有两根通讯线(发送端和接收端)
  • TX与RX要交叉连接
  • 当只需单向的数据传输时,可以只接一根通讯线
  • 当电平标准不一致时,需要加电平转换芯片

三、电平标准

  • 电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的关系,串口常用的电平标准有以下三种

  • TTL电平:+3.3V或5V表示1,0V表示0

  • RS232:-3~-15V表示1,+3-+15v表示0

  • RS485;两线压差+2-+6表示1,-2~-6表示0

四、串口参数及时序

  • 波特率:串口通讯的速率
  • 起始位:标志一个数据帧的开始,固定为低电平
  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
  • 校验位:用于数据验证,根据数据位计算得来
  • 停止位:用于数据帧间隔,固定为高电平

四、USART简介

  • USART 同步/异步收发器
  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX发出去,也可自动从RX接受引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器中
  • 自带波特率发生器,最高可达4.5Mbit/s
  • 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
  • 支持同步模式,硬件流控制,DMA、智能卡、IrDA、LIN
  • STM32C8T6 USART资源:USART1,USART2,USART3

五、波特率检测器

  • 发送器和接受器的波特率由波特率寄存器BRR里的DIV确定
  • 计算公式:波特率 = Fpclk2/1(16*DIV)

| 多设备 |
| USB | DP,DM | 半双工 | 异步 | 差分 | 点对点 |

二、硬件电路

  • 简单双向串口通讯有两根通讯线(发送端和接收端)
  • TX与RX要交叉连接
  • 当只需单向的数据传输时,可以只接一根通讯线
  • 当电平标准不一致时,需要加电平转换芯片

三、电平标准

  • 电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的关系,串口常用的电平标准有以下三种

  • TTL电平:+3.3V或5V表示1,0V表示0

  • RS232:-3~-15V表示1,+3-+15v表示0

  • RS485;两线压差+2-+6表示1,-2~-6表示0

四、串口参数及时序

  • 波特率:串口通讯的速率
  • 起始位:标志一个数据帧的开始,固定为低电平
  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
  • 校验位:用于数据验证,根据数据位计算得来
  • 停止位:用于数据帧间隔,固定为高电平

四、USART简介

  • USART 同步/异步收发器
  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX发出去,也可自动从RX接受引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器中
  • 自带波特率发生器,最高可达4.5Mbit/s
  • 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
  • 支持同步模式,硬件流控制,DMA、智能卡、IrDA、LIN
  • STM32C8T6 USART资源:USART1,USART2,USART3

五、波特率检测器

  • 发送器和接受器的波特率由波特率寄存器BRR里的DIV确定
  • 计算公式:波特率 = Fpclk2/1(16*DIV)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值